bebou

Le site web bebou.netlib.re - retour accueil

git clone git://bebou.netlib.re/bebou
Log | Files | Refs |

commit b73dd1d21e42b7a43c53e3b78de68876f15425c8
Auterice: Arthur Pons <arthur.pons@unistra.fr>
Date:   Sat, 27 Apr 2024 21:13:46 +0200

Premier commit

Diffstat:
A: | 32++++++++++++++++++++++++++++++++
Aarticle | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acommon | 32++++++++++++++++++++++++++++++++
Acontents/construire.sh | 712+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acontents/faq/index.sh | 404+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acontents/favicon.png | 0
Acontents/index.sh | 13+++++++++++++
Acontents/style.css | 492+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afaq | 25+++++++++++++++++++++++++
Alayouts/html | 26++++++++++++++++++++++++++
Amakefile | 29+++++++++++++++++++++++++++++
Apage | 35+++++++++++++++++++++++++++++++++++
12 files changed, 1907 insertions(+), 0 deletions(-)

diff --git a/: b/: @@ -0,0 +1,32 @@ +#! /usr/bin/env ./page +title: Un exemple de page +author: katzele +description: Un exemple de page Francium \(démontrant\) une partie des fonctionnalités +sectionmd: main + +# Un exemple de page dans Francium + +Cette page est un exemple simple de ce que l'on peut faire avec Francium + +Ici c'est du markdown : + + * des listes + * bidule + * chouette + +## Des sous-titres + +etc etc + +Ce qui va suivre c'est du mardown généré par cette commande shell exécutée +à la construction du site : + + uptime + +endsection + +uptime | save_md main + +sectionmd: footer +Copyright machin© bidule blablabla +endsection diff --git a/article b/article @@ -0,0 +1,107 @@ +#! /usr/bin/env dash + +# Chemin absolu du fichier css +# du point de vue du serveur web +# (et non pas dans le système de fichier +# du dépôt git) +STYLE=/style.css + +# Création du dossier temporaire dans lequel +# les fichiers contenant l'html des sections +the=$(mktemp -d) +trap "rm -rf $the" EXIT + +######################## +# PREMIÈRE CONFIGURATION +######################## + +# Choisissez une fonction save_* +# ou construisez la votre avec +# l'outil de votre choix + +# save_md() cmark >> "$the/$1" +save_md() { +cat > A +cat A |grep '^#' | +tail -n+2 | +sed -E '/^## / s/^## // + /^### / s/^### /- / + /^#### / s/^#### /-- /' > B +sed -E 's/^-+ //' B | +tr -d 'éàù,"è' | tr -d "'" | tr -s ' ' | tr ' [A-Z]' '-[a-z]' | +paste -d'\n' B - | +xargs -d'\n' printf '[%s](#%s) \n' > C +cat C A | lowdown >> "$the/$1" +rm A B C +} +# save_md() pandoc >> "$the/$1" +# save_asciidoc() asciidoctor >> "$the/$1" +# save_html() cat >> "$the/$1" + +# L'alias pour ouvrir une section +# Ouvre un heredoc avec pour délimiteur +# "endsection" et commande "save_*" +# Décommentez en un ou plusieurs selon +# le choix que vous avez fait au dessus + +# alias sectionasciidoc:='<<\endsection save_asciidoc' +alias sectionmd:='<<\endsection save_md' +# alias sectionhtml:='<<\endsection save_html' + +# Pareil mais le shell écrit dans la +# section s'exécutera +# alias sectionmd:='<<endsection save_md' + +############################### +# FIN DE PREMIÈRE CONFIGURATION +############################### + +# alias pour le DSL +# Se retrouvent à l'écriture des articles +# dans l'entête type : +# title: "titre de mon article" +alias title:="title" +# La fonction appelé par l'alias +# Ici permet simplement d'instancier +# une variable contenant la valeur du titre +title() title="$*" + +alias author:="author" +author() author="$*" + +alias description:="description" +description() description="$*" + +# Fonction appelé dans le layout +# Permet d'afficher à l'endroit +# souhaité la section passée en argument +# Par exemple : +# <main> +# $(the main) +# </main> +the() { + cd $the + cat "$@" +} + +# On charge la fonction déclarée dans layouts/html +# C'est le layout +. layouts/html + +# Je comprends pas bien ce que cette ligne fait +# Demander à Marc +# A priori elle teste si un argument a été donné +# à page et sinon elle fait `set -` +# Or dans le manuel de dash je ne trouve pas +# ce que ça fait +# En attendant on laisse +[ "$1" ] || set - + +# On exécute le code dans la page sh +# C'est maintenant que toutes les variables vont s'instancier +# et l'html être généré +. "$@" + +# On appelle la fonction layout de layouts/html +# pour afficher la page +layout diff --git a/common b/common @@ -0,0 +1,32 @@ +#! /bin/sh + +STYLE=/style.css + +the=$(mktemp -d) +trap "rm -rf $the" EXIT +FILE="$1" + +save_md() lowdown >> "$the/$1" + +alias sectionmd:='<<\endsection save_md' + +alias title:="title" +title() title="$*" + +alias subtitle:="subtitle" +subtitle() subtitle="$*" +alias author:="author" +author() author="$*" + +alias description:="description" +description() description="$*" + +the() { + cd $the + cat "$@" +} + +. layouts/html + +[ "$1" ] || set - + diff --git a/contents/construire.sh b/contents/construire.sh @@ -0,0 +1,712 @@ +#! article +title: Construire Francium +author: Arthur Pons + +sectionmd: main + +# Comment vous aussi vous auriez pu construire francium + +Imaginez vouloir construire une site et être doté.e d'un outil qui traduit le +format dans lequel vous préférez écrire en html. Peut-être que vous aimez +écrire en markdown et utilisez pandoc, cmark[^1] ou même le [script initial de +John Gruber]. Peut-être que vous aimez écrire en asciidoc et utilisez +asciidoctor. Peut-être que vous avez votre propre format avec votre propre +traducteur ! Comment, avec l'aide des outils Unix, créer votre propre +générateur de sites statiques ? + +## Générer une page + +### Les gabarits html et les métadonnées + +Vous avez un fichier index.md comme ceci : + + # Super document cool + + Blablabla + + * super liste + * trop cool + +et pouvez le convertir en faisant `cmark index.md` : + + <h1>Super document cool</h1> + <p>Blablabla</p> + <ul> + <li>super liste</li> + <li>trop cool</li> + </ul> + +Premier problème, si vous avez pour ambition d'avoir un site décemment +construit vous remarquerez qu'il manque de l'html. On pourrait vouloir ajouter +la balise `<html>` et `<meta>` pour l'encodage utf-8 par exemple. Ici +habituellement deux solutions s'offrent à vous. Soit vous comptez sur votre +traducteur pour le faire. C'est le cas par exemple de lowdown avec son option +`-s` (pour standalone) mais tous ne le font pas : + + <<-. lowdown -s + # titre + + [lien](katzele.netlib.re) + . + +qui donne + + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>Untitled article</title> + </head> + <body> + + <h1 id="titre">titre</h1> + + <p><a href="katzele.netlib.re">lien</a></p> + </body> + </html> + +soit vous utilisez un générateur de site existant. En réalité une troisième +s'offre à vous ! Résoudre ce problème à l'aide d'un outil que vous avez déjà +sous la main, le shell. + +Il existe en shell posix une syntaxe nommé `here-document` ou `heredoc`[^2]. +Cette syntaxe permet de créer des sortes de gabarits (ou templates ou layouts) +de la sorte : + + title="machin" + <<delim cat + blabla + le titre : $title + blabla + delim + +donne + + blabla + le titre : machin + blabla + +On notera que les variables shell appelées à l'intérieur sont étendues. Et bien +c'est également le cas des captures de commandes type `$(pwd)` ! On peut donc +imaginer l'utiliser pour insérer notre sortie de traducteur au sein d'un +gabarit html : + + title="Un article super" + <<delim cat + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>$title</title> + </head> + <body> + $(cmark index.md) + </body> + </html> + delim + +qui donne + + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>Un article super</title> + </head> + <body> + <h1>Super document cool</h1> + <p>Blablabla</p> + <ul> + <li>super liste</li> + <li>trop cool</li> + </ul> + </body> + </html> + +Hop pas besoin d'outils extérieur.\ +Si l'on fait de ce code un script on pourrait l'exécuter pour générer l'html. +Evidemment ce serait limité puisque l'on aurait toujours la sortie du fichier +index.md. On peut donc décider de passer en argument le nom du fichier markdown +et le titre tant qu'à faire. +On aurait donc un script du type : + + title="$1" + <<delim cat + [...] + $(cmark "$2") + [...] + delim + +Qu'on appelerait en faisant `./script "Un article super" ./index.md`. Si nos +besoins ne vont pas plus loin nous avons déjà quelque chose d'à peu près +fonctionnel. Pour rendre tout cela simple d'usage et mieux encapsuler +l'information dans des documents qui se suffisent à eux même nous allons +pousser plus loin. + +Vous remarquerez ici que l'information du titre réside dans votre tête, +à l'écriture de la commande et non pas, comme il est préférable, dans +le document lui-même. L'idéal serait qu'index.md puisse contenir toutes +ses informations, le markdown du corps de l'article mais également +les méta-données afférentes. C'est comme cela que fonctionnent la plupart +des générateurs de site avec des syntaxes propres. On voudrait pouvoir, +par exemple, écrire : + + title: "Un super article" + ---- + + # Super document cool + [...] + +et que la variable title de notre script prenne pour valeur "Un super article". +Pour ce faire on pourrait modifier notre script pour parser index.md et +en récupérer l'info que l'on souhaite. Par exemle + + title=$(grep '^title:' | cut -d':' -f2) + +donnerait la bonne valeur à la variable et + + $(sed -n '/----/,$ p` "$1" | grep -v '^----' | cmark) + +Parserait uniquement la partie markdown en ométtant les méta-données. C'est +pourtant sur une autre solution que nous allons partir, une solution qui nous +permettera plus de flexibilité par la suite et surtout la possibilité de +dynamiquement créer certaines parties du contenu markdown. + +### le format "à la francium" et la prolifération des scripts shell + +Cette autre solution consiste à faire du document index.md lui même +un script. Et oui, dans francium, si c'est pas une makefile, c'est +un script shell. Comme le dit le meme : + + +``` +shshsh shshsh shshsh sh sh shshsh shshsh sh sh sh sh +sh sh sh sh sh shsh sh sh sh sh sh shshsh +shsh shshsh shshsh sh shsh sh sh sh sh sh sh +sh shsh sh sh sh shs sh sh sh sh sh sh +sh sh sh sh sh sh sh shshsh shshsh shshsh sh sh + + Wait it's all shell scripts ? + / + 🚶🔫🚶 - Always has been +``` + +A ce stade on peut renommer index.md en index.sh, ça fera plus sens. L'idée +est dorénavant que notre script devienne une sorte d'interpréteur d'index.sh. +On l'appelera dorénavant "page". Il devra préparer le nécessaire pour que la +syntaxe de la page index.sh instancie les bonnes variables et génère le +nécessaire pour l'affichage final de l'html. + +Par exemple, on pourrait grâce à un alias, cacher derrière la syntaxe `title: +"Un super article"` une fonction qui créé la variable title. Pour cela +on peut écrire : + + alias title:="title" + title() title="$*" + +Ainsi, dans index.sh : + + title: "Un super article" + # devient + maketitle "Un super article" + # devient + title="Un super article" + +index.sh a créé sa propre variable. Ce système est généralisable à +n'importe quelle métadonnée. Si vous testez cette articulation tel quel +vous rencontrerez un problème. En effet, l'alias déclaré dans l'interpréteur +page n'est pas disponible dans le script index.sh si on l'appel en écrivant +`./$1` et en faisant `./page index.sh` par exemple. C'est parce que en le +faisant de cette manière là, index.sh est exécuté dans un sub-shell, sur un +processus différent ne partageant aucune variable, alias ou fonction +en commun avec son parent. Pour contourner ce problème on peut utiliser +le built-in shell `.`[^3] qui prend en argument le chemin d'un fichier. +Cette fonction va dérouler le code du fichier dans le script courant et +l'exécuter comme s'il y avait été écrit. Le code dans index.sh aura donc +bien accès à ce qui a été déclaré dans l'interpréteur page. Ainsi `./page +index.sh` fera : + + alias title:="title" + title() title="$*" + + . "$1" + + <<delim cat + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>$title</title> + [...] + delim + +deviendra + + alias title:="title" + title() title="$*" + + title: "Un super article" + ---- + + [...] + + <<delim cat + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>$title</title> + [...] + delim + +et `title: "Un super article"` se déroulera comme décrit précedemment et ainsi +de suite. Vous remarquerez peut-être que le `----` écrit ici paraît louche. A +priori tenter d'exécuter `----` dans le shell renverra une erreur puisqu'aucun +alias ou aucune[^4] commande ni fonction ne porte ce nom. Pour y remédier on +peut simplement créer un alias bidon `alias ----=":"` de façon à appeler le +builtin `:` qui, par définition, ne fait strictement rien. + +Super ! On sait comment gérer les métadonnées. En réalité on vient d'inventer une +sorte de [DSL] qui ressemble volontairement beaucoup à du [YAML] très simple. +Cette syntaxe est devenu très commune dans les générateurs de sites statiques. + +Maintenant qu'on a trouvé notre alternative à `title=$(grep '^title:' | cut +-d':' -f2)` il nous faut trouver celle à `$(sed -n '/----/,$ p` "$1" | grep -v +'^----' | cmark)`. Si on ne le fait pas l'exécution d'index.sh aura vite fait +de nous faire remarquer que le premier mot de notre page n'est pas une commande. + +Pour cela utilisons la même logique que pour les métadonnées, pensons à une +syntaxe qui nous convienne et créeons, en amont dans l'interpréteur le +nécessaire pour que cette syntaxe soit comprise et génère le bon contenu. +Pour pouvoir dire "attention, ce qui suit c'est du markdown" on peut +réutiliser les heredoc. Quelque de la sorte devrait générer l'html : + + title: "Un article super" + ---- + + <<delim cmark + # Ceci est du markdown + + blabla + delim + +Sauf qu'il nous faut capturer la sortie de lowdown pour pouvoir ensuite l'afficher +au bon endroit dans le heredoc template qui suit dans l'interpréteur page. On pourrait +le mettre dans une variable en l'entourant d'une capture de commande : + + corps=$(<<delim cmark + # Ceci est du markdown + + blabla + delim + ) + +et dans le template faire + + <main> + $(echo "$corps") + </main> + +mais vous conviendrez que ça n'est pas très élégant. On va donc opter pour +mettre le résultat dans un fichier temporaire. + + <<delim cmark > A + # Ceci est du markdown + + blabla + delim + [..] + <main> + $(cat A) + </main> + +ce qui permet d'adopter une syntaxe plus sympathique à l'aide d'un nouvel alias : + + section="<<endsection cmark > A" + [...] + section + # Ceci est du markdown + + blabla + endsection + +En ajoutant un petit `rm A` à la fin du script page on peut générer notre page +html en faisant `./page index.sh` sans souci. index.sh pourra se lire +convenablement bien et contiendra les infos à son propos en son sein. La +totalité du fichier jusqu'ici est : + + title: "Un super titre" + ---- + + section + # Ceci est du markdown + + blabla + endsection + +Avec ces éléments nous avons, à mon avis, tous les éléments d'un générateur de +site très simples. Afin d'organiser un peu les choses on pourrait déporter +le heredoc du layout dans un fichier à part, déclarant une fonction `layout`, +exécuté avec le builtin `.` et donc la fonction serait appelé à la fin. Cela +permet de séparer le layout du reste, le "fond" et la "forme" d'une certaine +manière. + +On a donc un fichier de layout, nommé html et dans le dossier layouts : + + layout { + <<@@ cat + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>$title</title> + </head> + <body> + $(cat A) + </body> + </html> + @@ + } + +Le fichier index.sh comme décrit juste au dessus et le scrit page : + + alias title:="title" + title() title="$*" + + . "$1" + + . layout/html + layout + rm A + +Si les sources sont dans contents et les fichiers cibles dans public il +suffirait ensuite de créer un script qui appelerait `./page X.sh > +X.html` sur toutes les pages sh nécessaires, avec `X` le nom du fichier pour, +construire le site. + +Il nous reste quelques améliorations à apporter à notre générateur. + +### L'encapsulation et les sections + +Premièrement, il manque une information dans le fichier index.sh, celle +de son interpréteur. De fait, le fichier est un script ne s'exécutant +correctement que s'il est passé en argument d'un autre[^5]. Il serait +bon de renseigner cette information dans le fichier. Pour ce faire nous +allons lui ajouter le shebang : + + #! page + +Ainsi il est possible de l'exécuter en faisant directement `./index.sh` ce qui +en réalité, du fait du fonctionnement du shebang, fera `./page ./index.sh` ce +qui revient à ce que nous faisions à la main jusque là. Cette façon de faire +est, selon moi, plus élégante, mais permet également de savoir que cette page +a été écrite pour être interprétée par un script nommé page ce qui, à défaut +de l'avoir, est toujours une bonne information à prendre. Ce système peut être +utilisé pour créer des typologies de pages (article, notes etc) qui seraient +générées de manières différentes. + +En réalité dans Francium c'est le shebang + + #! /usr/bin/env ./page + +qui est utilisé pour des raisons obscures de compatibilité avec les *bsd et les +mac[^7]. + +Deuxièmement, comment faire si l'on souhaite, sur une même page, écrire +du blocs de markdown qui doivent aller à deux endroits différents dans +le layout. Ce besoin est très courant dans les pages avec une structure plus +complexe, du type à avoir des div un peu partout, de façon à avoir une +présentation moins linéaire à l'aide de css par exemple. Pour répondre à ce +besoin que l'on a estimé être suffisament important pour être intégré au +générateur dans sa version la plus simple il faut ajouter un peu de complexité +dans l'interpréteur page. + +On souhaiterait pouvoir écrire dans index.sh : + + section: truc + ## Ce markdown va aller dans la section truc du layout + endsection + + section: bidule + ## Ce markdown va aller dans la section bidule du layout + endsection + +et par exemple dans le layout : + + layout { + <<@@ cat + [...] + <div id="joli-id-pour-du-css"> + $(cat truc) + </div> + [...] + <div id="autre-id-pour-du-css"> + $(cat bidule) + </div> + @@ + } + +On voit que intuitivement on passe une sorte d'argument à notre alias +section[^6]. Il faudrait pouvoir, sur la base de cet argument, stocker +l'html généré par chacune des sections pour pouvoir les appeler séparément +dans le layout. Pour pouvoir traiter cette donnée comme un argument +l'alias ne suffira plus, il faudra passer par une fonction qu'on pourrait +par exemple nommer `save_md` puisqu'on lui donne du markdown en entrée : + + alias section:="<<endsection save_md" + save_md() cmark >> "$1" + +Ainsi dans index.sh : + + section: truc + blabla + endsection + # Devient + <<endsection save_md truc + blabla + endsection + # Puis + <<endsection cmark >> truc + blabla + endsection + +Les captures de commandes que l'on a fait dans le layout vont donc +fonctionner d'elles mêmes. On peut ainsi ouvrir et fermer autant +de sections que l'on veut, y compris plusieurs fois la même grâce +à l'opérateur de redirection `>>` qui concatène plutôt que ne remplace. +Afin d'éviter que les fichiers ne se marchent sur les pieds si +l'on a plusieurs articles, de polluer l'espace de travail et de potentiellement +engorger la mémoire de votre ordinateur on peut faire en sorte que tout +cela se produise dans un dossier temporaire, sur des fichiers temporaires +tous détruits une fois la génération finie. Pour on créer au début de page +un dossier temporaire de travail avec `mktemp`, on dit au shell de le supprimer +si le processus reçoit le signal `EXIT` (une fois qu'il termine quoi) et on +fait une petite fonction pour le layout puisque l'on ne peut plus juste cat +les fichiers comme s'ils existaient à la racine de notre projet : + + the=$(mktemp -d) + trap "rm -rf $the" EXIT + + the() { + cd $the + cat "$@" + } + +Dans le layout on aura `$(the truc)` au lieu de `$(cat truc)`[^7]. + +Et voilà, à une exception près[^8] vous avez recréé absolument tout Francium +dans sa version non étendue. Bravo ! + +## Automatiser la génération, le makefile + +Il y aurait pleins de manières de partir de cet existant et d'automatiser +la création d'un site de plusieurs fichiers .sh. Un simple script shell +pourrait faire l'affaire. Cependant, pour des raisons pédagogique et, +potentiellement de performances, nous avons opté pour utiliser make. + +Make est un programme spécifiquement créé pour gérer la construction de +logiciels. Initialement fait pour compiler des programmes en C à une époque où +les ressources étaient assez rares, make permet à l'utilisateurice de déclarer +les règles de dépendances entre les différents éléments de son logiciel. make +créera avec ces règles un graph de dépendances lui permettant, lorsqu'il est +appelé, de ne recréer que le strict nécessaire ainsi économisant du temps de +calcul. Sur le principe la compilation de logiciels et la construction d'un +site statique ne sont pas différents. On peut donc faire usage de make pour +créer orchestrer la génération de notre site. + +Tous le nécessaire pour que make puisse fonctionner doit être inscrit +dans un fichier nommé `makefile` à la racine du projet. + +Francium utilise Gnu Make et quelques syntaxes lui sont très spécifiques. + +### Le but + +Le but est que make fasse automatiquement les appels aux articles +et place leurs sorties dans les bons fichiers de destinations. Par exemple, + + ./contents/index.sh > public/index.sh + +Et ainsi pour tous les fichiers .sh qui existent dans l'arborescence à +l'intérieur de contents. + +Dans un second temps on souhaite qu'il copie bêtement tous les autres +fichiers présents dans l'arborescence comme les images par exemple : + + cp contents/super_image.jpg public/super_image.jpg + +Dans notre cas tout simple les fichiers .css et le favicon seront +gérés de la même manière que tous les images n'étant pas des .sh. + +### Lister nos fichiers sources + +La première étape est de lister toutes les sources de notre site. +On souhaite faire des choses différentes selon que le fichier soit +un .sh ou pas alors on créé deux variables. Dans sources on met la liste des +fichiers sh, dans annexes les autres : + + sources != find contents -type f -name '*.sh' + annexes != find contents -type f -not -name '*.sh' + +La syntaxe `!=` dit à make que ce qui suit est une commande à faire exécuter +par le shell et non pas des fonctions make. On utilise donc ici le find +que vous pourriez utiliser dans votre terminal en temps normal. + +Une fois ces deux listes obtenues, on cherche à créer deux nouvelles listes +contenant les chemins des fichiers correspondants à faire servir par le +serveur web. On les appelera fichiers cibles. Par exemple, si l'on +a le chemin "contents/blog/article.sh" dans la variable sources, +il faudra créer le chemin "public/blog/article.html". Autrement dit on remplace +contents par public et l'extension .sh par l'extension .html. Pour les autres +fichiers seul le premier dossier du chemin est à changer. + +Pour le faire on fait usage des "substitutions references" de GMake dont la syntaxe est : + + variablecréé = ${variableinitiale:abc%uvw=efg%xyz} + +Ici, pour toutes les lignes dans la variableinitiale, make cherchera les +caractères `abc` et `uvw` et attribuera tout ce qu'il y a entre à `%`. Il +remplacera ensuite tous les `abc` par `efg` et les `uvw` par `xyz` en +conservant la partie `%`. Ainsi, si l'on reprend notre exemple précédent on +identifie que la partie commune est `blog/articl` et qu'il faut changer ce qu'il +y a avant et après. On peut donc écrire : + + pageshtml = ${sources:contents/%.sh=public/%.html} + annexescibles = ${annexfiles:contents/%=public/%} + +Vous voyez que pour les annexes pas besoin de modifier l'extension donc pas +besoin de la partie après le `%`. On a donc dorénavant dans `pageshtml` et +`annexescibles` la liste des chemins des cibles. + +C'est maintenant que la partie intéressante de make commence, les règles ! La +syntaxe générale d'une règle est la suivante : + + cible : liste dépendances ; commandes + +ou + + cible : liste dépendances + commandes + +Pour chacun des chemins de cibles construits précédemment make va chercher une +règle permettant de construire le fichier en question. Pour cela il cherche une +règle ayant pour cible le même chemin. Une fois qu'il a trouvé une règle qui +correspond, il vérifie si la cible existe. + + * Si elle n'existe pas il cherche à construire toutes ses dépendances puis + exécute la commande. + * Si la cible existe mais que certaines de ses dépendances n'existent pas il + va les construire d'abord et passer au point suivant. + * Si la cible existe déjà et que les dépendances existent il vérifie si ses + dépendances ont été modifiées après sa dernière modification. Si oui alors + make suppose que la cible n'est pas à jour (ses sources sont plus récentes + qu'elle même) et make relance la commande. + +A noter que tout cela ce fait de façon récursive. Quand make tente de créer une +dépendance il suit les mêmes règles. Si la dépendance n'existe pas il faut donc +qu'il existe une règle pour la créer. + +Pour créer une page html la règle pourrait la suivante : + + public/blog/article.html : contents/blog/article.sh layouts/html + mkdir -p public/blog; contents/blog/article.sh > public/blog/article.html + +Vous noterez que la cible dépend de layouts/html de façon à ce qu'elle +soit reconstruite si le layout change. + +Vous pourrez penser "mais on ne va pas écrire les règles pour toutes +les pages non ?" et vous auriez raison. Il est possible d'écrire des +règles génériques, pouvant matcher plusieurs chemins de plusieurs cibles. +Par exemple, la règle suivante conviendra pour créer tous les fichiers +html : + + public/%.html : contents/%.sh layouts/html + mkdir -p $(shell dirname $@); $< > $@ + +De la même manière qu'auparavant on généricise les chemins à l'aide de `%`. On +récupère le chemin du dossier du fichier cible avec dirname et on fait usage +des variables automatiques `$<` et `$@` faisant références respectivement à la +première dépendance et la cible. + +Pour les autres fichiers, de façon similaire : + + public/% : contents/% + mkdir -p $(shell dirname $@); cp $< $@ + +Si vous écrivez un makefile de la sorte et tenter de l'exécuter vous vourrez +qu'il ne créera pas tout le site. Make créé les cibles qu'on lui donne en +argument. `make public/index.html` déclenchera la règle correspondant à cette +cible si elle existe. Cela dit par défaut sans argument make créé uniquement la +cible de la première règle qu'il rencontre dans le makefile, dans l'ordre du +fichier. Comment en sachant cela, forcer la création de toutes les cibles dans +les variables pageshtml et annexescibles ? + +En utilisant les propriétés des règles décrites précédemment. + + 1. Si make n'a pas d'arguement, la première règle du fichier est déclenchée. + 2. Si la cible d'une règle n'existe pas, il va essayer de la créer. On peut + donc en déduire que si la commande de la règle ne créer pas la cible la règle + sera toujours déclenchée. + 3. Make cherche d'abord à créer toutes les dépendances d'une cible avant de + créer la cible. + +On en conclu que si l'on écrit une règle qui + + 1. Précède toutes les autres + 2. A une commande qui ne créé pas de fichier ayant pour chemin la cible. + Aucune commande fera très bien l'affaire. + 3. A pour dépendances toutes les cibles que l'on souhaite construire. + +Alors lorsque l'on lancera make, il cherchera à construire toutes +les cibles à chaque fois. + +Cette règle est très courante dans les makefiles et est généralement nommée +par convention "all", mais elle pourrait s'appeler "toto". Pour lister les +dépendances on peut utiliser les variables déjà existantes : + + all: ${pageshtml} ${annexescibles} + +En utilisant les mêmes propriétés nous allons écrire une règle qu'il faudra +appeler volontairement en passant sa cible en argument à make, se déclenchera +toujours, et exécutera une commande. Cette règle permettra de nettoyer le +dossier de destination "public" : + + clean:; rm -r public/* + +Si elle n'est pas première dans le fichier et que la cible clean n'est une +dépendance d'aucune autre règle, elle ne se déclenchera qu'en faisant `make +clean`. Puisque la commande ne créé pas de fichier ayant pour chemin la cible +alors elle se déclenchera toujours. Cela dit, puisqu'elle n'a pas non plus +de dépendances, s'il se trouve qu'un jour un fichier "clean" existe à la +racine du projet, il sera considéré comme toujours à jour. Aucune dépendance +ne pourra être potentiellement plus récente que lui et la règle ne se +déclenchera plus. C'est pour palier ce genre de scénarios qu'il existe une +directive `.PHONY` qui permet de lister ce genre de règles[^9]. Ainsi +make saura qu'il faut, pour ces règles, ignorer la préexistence de la cible. + +Et voilà, vous avez réécri le makefile de francium tel qu'il existe aujourd'hui +! + +[^1]: que l'on utilisera pour les exemples dans ce document +[^2]: documentée ici https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 +[^3]: oui le nom de la fonction est simplement `.`. +[^4]: du moins très probablement aucune +[^5]: quand bien même la syntaxe a été écrite de façon à ce qu'un.e humain.e puisse le comprendre +[^6]: auquel on a ajouté un `:` pour ressemble aux autres déclarations +[^7]: Voir: https://www.in-ulm.de/~mascheck/various/shebang/#interpreter-script ou le commit 4bd895 +[^8]: la ligne `[ "$1" ] || set -` que je ne m'explique pas +[^9]: même s'il est très improbable qu'un fichier all ou clean se faufile dans votre projet, on sait jamais. +[script initial de John Gruber]: https://daringfireball.net/projects/markdown/ +[DSL]: https://en.wikipedia.org/wiki/DSL +[YAML]: https://en.wikipedia.org/wiki/YAML + +endsection + +sectionmd: footer + Footer +endsection diff --git a/contents/faq/index.sh b/contents/faq/index.sh @@ -0,0 +1,404 @@ +#! /usr/bin/env ./faq +title: Foire aux questions +author: bébouse +description: Les réponses à toutes les questions que vous vous posez au sujet du village des bébous +subtitle: \(dont on pense qu\'elles seront fréquemment posées\) + +sectionmd: main + +Dans toute cette FAQ toutes les commandes à taper sont précédée d'un `$ ` et +les résultats qui s'affichent simplement dans le terminal ne le sont pas. + +## Comment obtenir un compte/site + +Plusieurs conditions doivent être réunies pour que vous puissiez obtenir un compte : + + 1. Vous devez connaître personnellement une personne actuellement dans le village et le village doit avoir envie de vous y accueillir + 2. Il doit rester des ressources matérielles pour vous accueillir (espace disque, ram, cpu, bande passant) + 3. Vous devez être en mesure de [fournir une clef ssh publique](/faq/#comment-crer-et-communiquer-une-paire-de-clef-ssh) + 4. Vous devez être en mesure d'expliquer pourquoi vous voulez rejoindre le serveur + +Si toutes ces conditions sont réunies vous pouvez nous envoyer votre clef +publique et nous rejoindre :) + +## Comment créer et communiquer une paire de clef ssh + +L'unique prérequis est d'avoir [openSSH](https://www.openssh.com/) installé sur votre machine. + +Le but ici est de créer ce que l'on nomme une paire de clef ssh qui permettra de vous +authentifier sur le serveur. C'est un peu comme un mot de passe mais en mieux. La +procédure va créer sur votre ordinateur deux fichiers intrinsèquement liés. Une +clef dite publique et une clef dite privée. La clef publique est celle qu'il +faudra communiquer aux personnes sur le serveur pour obtenir un accès. Il n'y a +aucun risque à la communiquer au monde entier, elle est faite pour ça. **La clef +privée est elle très secrète. Il ne faut absolument jamais la communiquer à +qui que ce soit d'autre ni la perdre. Il n'est pas possible de la regénérer +si elle est perdue.** + +Tout se fera dans le terminal. Ouvrez donc votre terminal préféré (parfois en +faisant `ctrl+alt+t`). Désolé, je vais mettre les exemples en anglais. + +Pour vérifier si vous avez openSSH lancez `ssh -V`. Si vous voyez quelque chose +du type `OpenSSH_9.2p1 Debian-2+deb12u2, OpenSSL 3.0.11 19 Sep 2023` ou quoi +que ce soit d'autre que `command not found` ou message d'erreur similaire alors +c'est bon ! Sinon il va falloir installer openSSH à l'aide de votre +gestionnaire de paquet. Par exemple sous debian `sudo apt install +openssh-client` fera l'affaire. + +C'est maintenant que vous allez choisir un nom de compte et, par défaut, +votre nom de domaine. Il doit ne contenir que des minuscules de *a* à *z* et/ou +des chiffres. Aucun caractère spécial, aucune majuscule. Si votre nom de compte +est "alice" alors par défaut votre site sera accessible à l'adresse +"http://alice.bebou.netlib.re". [Il est possible de la changer +ensuite](/faq/#changer-de-sous-domaine). En partant du principe que vous voulez +nommer votre compte et votre sous-domaine "alice" vous pouvez lancer la +commande suivante : + + $ ssh-keygen -t ed25519 -C alice + +Elle va vous demander un ensemble d'informations. Ce qui est écrit entre +parenthèses est le comportement par défaut. S'il convient il suffit d'appuyer +sur entrer pour passer à la suite. Sinon il faut soit même entrer ce que l'on +souhaite. + +Premièrement, l'emplacement de la clef. Ici `votre_utilisateur` est le nom de +votre compte sur votre machine perso. Vous devez rentrer le chemin vers la clef +(le fichier) à créer. Si c'est votre première clef ssh vous pouvez simplement +appuyer sur entrer. Sinon j'imagine que vous savez un peu ce que vous faites, entrez +un chemin qui convient et faites attention à ne pas écraser une clef existante : + + Generating public/private ed25519 key pair. + Enter file in which to save the key (/home/votre_utilisateurice/.ssh/id_ed25519): + +Deuxièmement la commande vous demandera une phrase de passe pour chiffrer la clef. +Autrement dit seules les personnes connaissant cette phrase de passe et ayant +accès à la clef - à priori uniquement vous - pourront utiliser la clef pour +s'authentifier sur le serveur sur le compte correspondant. Si vous choisissez +de ne pas mettre de phrase de passe toute personne ayant une copie de votre clef +privée pourra se connecter sur votre compte sans plus d'informations nécessaires. +Si vous choisissez de mettre une phrase de passe chaque utilisation de la clef +nécessitera d'insérer cette phrase pour la débloquer et pouvoir l'utiliser. +C'est une protection supplémentaire en cas de perte et/ou de vol de la clef +privée. *Il est recommandé de mettre une phrase de passe même si cela n'est +pas nécessaire*. Il est également recommandé de choisir une bonne phrase de +passe, c'est à dire aussi aléatoire que possible et d'au moins 6/7 mots[^2]. +Si vous mettez une phrase de passe et que vous l'oubliez il sera **impossible +de débloquer la clef privée**. Il est donc important de faire l'effort de +l'apprendre par coeur. + + Enter passphrase (empty for no passphrase): + Enter same passphrase again: + + + Your identification has been saved in /home/votre_utilisateurice/.ssh/id_ed25519 + Your public key has been saved in home/votre_utilisateurice/.ssh/id_ed25519.pub + The key fingerprint is: + SHA256:jEl7BWye0joCmY+VQcxyGG9RqmOhTO8TD5bCS6UM38I alice + The key's randomart image is: + +--[ED25519 256]--+ + | .*o.... | + | o.=o o. | + |...*+o.+ .. | + |*+B=+..=+. | + |.BE@. +oS | + |..*o* o. | + | . o o . | + | . | + | | + +----[SHA256]-----+ + +Oh le joli dessin ! Comme indiqué vous avez dorénavant un clef privée +`id_ed25519` et la clef publique associée `id_ed25519.pub`. + +Le contenu de la clef publique ressemble à quelque chose du style + + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEMQgNDrqMgWCC4EjuXZ/e08/dZOhUqSa/z7w8sAHolL alice + +Tout ce qui suite la longue chaîne de caractères "aléatoire"[^3] servira à vous +donner un nom de compte. Si vous changez d'avis c'est maintenant ou jamais de +l'éditer. + +Pour qu'une personne déjà sur le serveur vous donne les accès il faudra lui +communiquer le fichier `id_ed15519.pub`. Peu importe le canal utilisé, c'est un +simple fichier texte. Vous pouvez copier coller son contenu dans une +conversation, envoyer le fichier en tant que tel en pièce-jointe d'un mail +etc. + +Plus qu'à attendre qu'on vous ajoute ! + +## Comment se connecter au serveur + +Pour se connecter à votre compte sur le serveur il faut d'abord [remplir les +conditions pour pouvoir obtenir un +compte](/faq/#comment-obtenir-un-comptesite), avoir [créé une paire de clef et +l'avoir envoyé à une personne déjà sur le +serveur](/faq/#comment-crer-et-communiquer-une-paire-de-clef-ssh). + +Si tout cela est déjà fait vous devriez pouvoir vous y connecter en ssh en faisant : + + $ ssh alice@bebou.netlib.re -p 1459 + +Si vous avez mis une phrase de passe il faudra l'insérer, sinon vous devriez +arriver sur votre compte ! + +Pour éviter d'avoir à systématiquement taper tout cela quand vous voulez vous +connecter vous pouvez ajouter une entrée dans le fichier +`/home/votre_utilisateurice/.ssh/config` à l'aide d'un éditeur de texte. Si ce +fichier n'existe pas il faudra le créer. A l'intérieur vous pouvez écrire : + + host bebou + hostname bebou.netlib.re + user alice + port 1459 + +Ainsi vous n'aurez plus qu'à taper `ssh bebou` pour vous connecter. `ssh` fera +automatiquement le lien entre le "host" `bebou` et l'url, le port et le nom du +compte. + +## Comment modifier votre site + +Si vous venez d'ouvrir un compte vous devriez voir une page extrêmement simple +à `votrecompte.bebou.netlib.re`. Comment la modifier ? + +### Directement depuis le serveur + +Nous documentons cette possibilité parce qu'elle peut avoir son utilité mais nous +recommandons de [travailler en local](/faq/#en-local-puis-en-poussant-les-modifications-sur-le-serveur). + +Quand vous vous [connectez sur le serveur](/faq/#comment-se-connecter-au-serveur) vous +arriverez dans votre dossier personnel - `/home/alice/`. Ici vous pouvez utiliser +la commande `ls` pour vérifier ce qu'il s'y trouve : + + $ ls + alice.bebou.netlib.re choixutil createtmuxsession + +`alice.bebou.netlib.re` est le dossier contenant votre site. Tous les fichiers +qui s'y trouvent sont accessibles sur `http://alice.bebou.netlib.re`. Par défaut +le serveur web affiche `index.html` s'il existe. Vous pouvez constatez qu'il existe +déjà : + + $ ls alice.bebou.netlib.re + index.html + +Pour vérifier son contenu vous pouvez utiliser `cat` : + + $ cat alice.bebou.netlib.re/index.html + page de alice + +> Si vous fatiguez après avoir tapé ces commandes assez longues sachez qu'il est +> possible d'auto compléter les chemins des fichiers[^4] en faisant une +> tabulation. + +Pour modifier le contenu depuis le terminal on peut utiliser un éditeur de texte +dans le terminal, par exemple nano : + + $ nano alice.bebou.netlib.re/index.html + GNU nano 7.2 alice.bebou.netlib.re/index.html + page de alice + + + [ Lecture de 1 ligne ] + ^G Aide ^O Écrire ^W Chercher ^K Couper ^T Exécuter ^C Emplacement + ^X Quitter ^R Lire fich. ^\ Remplacer ^U Coller ^J Justifier ^/ Aller ligne + +Il est possible ici de modifier le fichier en se déplaçant avec les flèches du +clavier et en tapant le texte que l'on souhaite insérer. Vuos pouvez ensuite +vous référez aux raccourcis en bas de la fenêtre pour sauvegarder le fichier et +sortir, en sachant qu'ici `^` veut dire la touche control. Ainsi pour écrire le +fichier dans sa nouvelle version il faut faire `ctrl+o` et valider le chemin du +fichier (simplement appuyer sur entrer). Pour ensuite quitter vous opuvez faire +`ctrl+x`. Alternativement en faisant directement `ctrl+x` nano vous demandera +si vous voulez sauvegarder le fichier ou pas, ce que vous pouvez accepter en +appuyant sur `o` et vous pourrez quitter en appuyant sur la touche entrer. + +une fois le fichier sauvegarder vous pouvez accéder à `alice.bebou.netlib.re` +via n'importe quel navigateur et constater le changement ! + +### En local puis en poussant les modifications sur le serveur + +Vous conviendrez que la méthode précédente est assez fastidieuse. Il +faut se faire à l'utilisation d'un éditeur de texte dans le terminal, +il n'est pas possible de travailler hors ligne puisqu'il vous pouvoir +accéder au serveur etc. **Nous recommandons donc de travailler en local**. + +Par travailler en local vous crééerez votre site sur votre ordinateur pour +ensuite déposer tous les fichiers d'un coup sur le serveur. Admettons que vous +souhaitiez avoir une page d'accueil très simple avec un titre et une image. +Vous pouvez créer un dossier `site` dans votre répertoire maison et y mettre le +document `index.html` avec le contenu suivant : + + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>Le site d'Alice</title> + </head> + <body> + <h1>Bienvenu sur mon site !</h1> + <p>Voici mon chat</p> + <img src="/chat.jpg"> + </body> + </html> + +Ici vous serez tenter de vérifier si tout fonctionne bien avant de l'envoyer sur +le serveur. Pour cela pas de solution magique, il vous faut faire tourner un +serveur web localement sur votre ordinateur. Pour cela voir [la partie dédiée +à cette question](/faq/#comment-tester-localement-son-site). + +Pour ensuite envoyer tout le contenu du dossier `site` vers le bon dossier sur +le serveur nous allons utiliser [`rsync`](https://rsync.samba.org/). Sur debian +vous pouvez l'obtenir via apt : `sudo apt install rsync`. + +Une fois rsync installé vous pouvez synchroniser les dossiers locaux et distants +en faisant : + + rsync -hvr ~/site/ alice:~/alice.bebou.netlib.re/ + Enter passphrase for key '/home/votre_utilisateurice/.ssh/id_ed25519': + sending incremental file list + site/ + site/chat.jpg + site/index.html + + sent 933 bytes received 58 bytes 31,46 bytes/sec + total size is 735 speedup is 0,74 + +`-h` dit à rsync d'afficher les tailles transférées en format lisible par des +humain·es. `-v` d'afficher plus d'infos. `-r` de copier récursivement tout ce +qu'il y a dans le dossier local. `~/site/` est le dossier dans lequel vous avez +mis les fichiers à copier. `alice` est le nom du host [que vous avez +possiblement préalablement créé dans `.ssh/config`](/faq/#comment-se-connecter-au-serveur) et +`~/alice.bebou.netlib.re/` le dossier dans lequel copier le contenu de `site`. + +Si vous n'avez pas renseigné de phrase de passe à la [génération de votre +clef](/faq/#comment-crer-et-communiquer-une-paire-de-clef-ssh) elle ne vous +sera pas demandée. Si vous n'avez pas ajouté d'entrée dans votre `.ssh/config` +de façon à pouvoir simplement écrire vous pouvez écrire `alice:` vous pouvez +écrire + + rsync -hvr ~/site/ --port=1459 alice@bebou.netlib.re:~/alice.bebou.netlib.re/ + +C'est un peu plus long et embêtant. Il est **important de mettre des `/` à la +fin des chemins sinon les fichiers ne se copieront pas comme il faut**. C'est +bien `~/site/` et `~/alice.bebou.netlib.re/` et non pas `~/site` et +`~/alice.bebou.netlib.re`. + +Si vous avez tout fait comme il faut vous devriez pouvoir voir votre +nouveau site à l'url correspondante, bravo ! + +## Comment ajouter une personne au serveur + +Votre copaine voudrait nous rejoindre, tout le monde l'aime et iel a un projet +trop cool ? Iel vous a envoyé sa clef publique ? Vous mourrez d'envie de lui +créer un compte de suite là maintenant ? + +Rien de plus simple, connectez vous au compte `pi` possédant les droits +d'administration : + + $ ssh pi@bebou.netlib.re -p 1459 + +puis lancez la commande menu : + + $ menu + > + shell + supprimer un compte + migrer + ajouter un compte + +Pour sélectionner une action commencez par taper son nom ou naviguez vers le bas +avec les flèches du clavier. Quand vous êtes sur `ajouter un compte` (quand cette +ligne est en surbrillance), appuyez sur "entrer". Vous verrez ensuite : + + > + en copiant/collant + depuis un fichier + +Si vous choisissez en copiant/collant il suffira de coller la clef publique comme ceci : + + copier/coller la clef ici : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEMQgNDrqMgWCC4EjuXZ/e08/dZOhUqSa/z7w8sAHolL alice + +puis d'appuyer sur la toucher entrer. Le serveur va créer le compte et automatiquement +vous déposer dans le salon "accueil" du serveur irc. Vous pouvez faire un petit coucou +au nom de votre copaine ou directement fermer a session en faisant `ctrl+d`. + +Si vous choisissez depuis un fichier existant sur le serveur il faudra renseigner +son chemin : + + chemin vers le fichier : /home/pi/alice.pub + +Dans ce cas pas d'ouverture du client irc mais ne vous inquiétez pas. + +## Supprimer son compte + +***Attention : toute suppression est définitive*** + +Si vous voulez nous quitter, ou si pour une raison légitime il est important de +supprimer le compte d'une autre personne connectez vous au compte pi + + $ ssh pi@bebou.netlib.re -p 1459 + +puis lancez la commande menu + + $ menu + > + shell + supprimer un compte + migrer + ajouter un compte + +choisissez "supprimer un compte" puis le compte en question ("annuler" ferme +tout) : + + Dire adieu à qui ? + > + annuler + machin + alice + +Vous devrez confirmer la suppression. En appuyant sur `o` puis entrer vous +supprimerez définitivement toutes les données du compte. En appuyant sur quoi +que ce soit d'autre puis entrer vous annulerez tout. + + Supprimer l'utilisateurice alice ? o=oui, quoi que ce soit d'autre=non : + +Puis une dernière confirmation (on sait jamais) + + Vraiment vraiment supprimer alice ? écrire 'oui' pour confirmer : oui + +Hop, tout est supprimé ! + +## Changer de sous-domaine + +Si vous voulez que votre site soit accessible à (http://cool.bebou.netlib.re) et +plus à (http://alice.bebou.netlib.re) vous pouvez vous connecter sur le compte pi : + + + +puis lancer la commande menu : + + $ menu + > + shell + supprimer un compte + changer de sous-domaine + ajouter un compte + +choisir "changer de sous-domaine" et selectionner votre site parmi la liste : + + du sous-domaine + > + alice + machin + +et finalement renseigner le nouveau sous-domaine : + + vers : cool + +Votre site devrait instantanément être dispo à la nouvelle adresse. +[L'annuaire](http://bebou.netlib.re/pagesjaunes) devrait quant à lui se mettre +à jour maximum cinq minutes après l'opération. + +[^2]: Pour en savoir plus vous pouvez visionner [une conférence sur le sujet](https://podv2.unistra.fr/video/23983-techdays-6-quest-ce-qui-fait-un-bon-mot-de-passe-arthur-pons/) +[^3]: ici "alice" +[^4]: et certaines commandes ! diff --git a/contents/favicon.png b/contents/favicon.png Binary files differ. diff --git a/contents/index.sh b/contents/index.sh @@ -0,0 +1,13 @@ +#! /usr/bin/env ./page +title: Bébous town +author: bébouse +description: La page d\'accueil du club des bébous +sectionmd: main + +Le village des bébous est un serveur communautaire auto-hébergé. + +[La liste des sites](/pagesjaunes)\ +[Des statistiques sur le serveur](/stats)\ +[Foire aux questions](/faq) + +endsection diff --git a/contents/style.css b/contents/style.css @@ -0,0 +1,492 @@ +/* Global variables. */ +:root, +::backdrop { + /* Set sans-serif & mono fonts */ + --sans-font: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, + "Nimbus Sans L", Roboto, "Noto Sans", "Segoe UI", Arial, Helvetica, + "Helvetica Neue", sans-serif; + --mono-font: Consolas, Menlo, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + --standard-border-radius: 5px; + + /* Default (light) theme */ + --bg: #fff; + --accent-bg: #f5f7ff; + --text: #212121; + --text-light: #585858; + --border: #898EA4; + --accent: #0d47a1; + --accent-hover: #1266e2; + --accent-text: var(--bg); + --code: #d81b60; + --preformatted: #444; + --marked: #ffdd33; + --disabled: #efefef; +} + +/* Dark theme */ +@media (prefers-color-scheme: dark) { + :root, + ::backdrop { + color-scheme: dark; + --bg: #212121; + --accent-bg: #2b2b2b; + --text: #dcdcdc; + --text-light: #ababab; + --accent: #ffb300; + --accent-hover: #ffe099; + --accent-text: var(--bg); + --code: #f06292; + --preformatted: #ccc; + --disabled: #111; + } + /* Add a bit of transparency so light media isn't so glaring in dark mode */ + img, + video { + opacity: 0.8; + } +} + +/* Reset box-sizing */ +*, *::before, *::after { + box-sizing: border-box; +} + +html { + /* Set the font globally */ + font-family: var(--sans-font); + scroll-behavior: smooth; +} + +/* Make the body a nice central block */ +body { + color: var(--text); + background-color: var(--bg); + font-size: 1.15rem; + line-height: 1.5; + display: grid; + grid-template-columns: 1fr min(50rem, 90%) 1fr; + margin: 0; +} + +body > * { + grid-column: 2; +} + +/* Make the header bg full width, but the content inline with body */ +body > header { + background-color: var(--accent-bg); + border-bottom: 1px solid var(--border); + text-align: center; + /*padding: 0 0.5rem 2rem 0.5rem;*/ + grid-column: 1 / -1; +} + +body > header > *:only-child { + /*margin-block-start: 2rem;*/ +} + +body > header h1 { + max-width: 1200px; + margin: 1rem auto; +} + +body > header p { + max-width: 40rem; + margin: 1rem auto; +} + +/* Add a little padding to ensure spacing is correct between content and header > nav */ +main { + padding-top: 1.5rem; +} + +body > footer { + margin-top: 4rem; + padding: 2rem 1rem 1.5rem 1rem; + color: var(--text-light); + font-size: 0.9rem; + text-align: center; + border-top: 1px solid var(--border); +} + +/* Format headers */ +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2.6rem; + margin-top: 3rem; +} + +h3 { + font-size: 2rem; + margin-top: 3rem; +} + +h4 { + font-size: 1.44rem; +} + +h5 { + font-size: 1.15rem; +} + +h6 { + font-size: 0.96rem; +} + +p { + margin: 1.5rem 0; +} + +/* Prevent long strings from overflowing container */ +p, h1, h2, h3, h4, h5, h6 { + overflow-wrap: break-word; +} + +/* Fix line height when title wraps */ +h1, +h2, +h3 { + line-height: 1.1; +} + +/* Reduce header size on mobile */ +@media only screen and (max-width: 720px) { + h1 { + font-size: 2.5rem; + } + + h2 { + font-size: 2.1rem; + } + + h3 { + font-size: 1.75rem; + } + + h4 { + font-size: 1.25rem; + } +} + +/* Format links & buttons */ +a, +a:visited { + color: var(--accent); +} + +a:hover { + text-decoration: none; +} + +/* Set the cursor to '?' on an abbreviation and style the abbreviation to show that there is more information underneath */ +abbr[title] { + cursor: help; + text-decoration-line: underline; + text-decoration-style: dotted; +} + +/* Format navigation */ +header > nav { + font-size: 1.2rem; + line-height: 2; + /*padding: 1rem 0 0 0;*/ +} + +/* Use flexbox to allow items to wrap, as needed */ +header > nav ul, +header > nav ol { + align-content: space-around; + align-items: center; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + list-style-type: none; + margin: 0; + padding: 0; +} + +/* List items are inline elements, make them behave more like blocks */ +header > nav ul li, +header > nav ol li { + display: inline-block; +} + +header > nav a, +header > nav a:visited { + /*margin: 0 0.5rem 1rem 0.5rem;*/ + /*border-left: 1px solid var(--border); + border-right: 1px solid var(--border);*/ + /*border-radius: var(--standard-border-radius);*/ + color: var(--text); + display: inline-block; + padding: 0.7rem 2.5rem; + text-decoration: none; +} + +header > nav a:hover, +header > nav a.current, +header > nav a[aria-current="page"] { + /*border-color: var(--accent);*/ + color: var(--accent); + cursor: pointer; +} + +/* Reduce nav side on mobile */ +@media only screen and (max-width: 720px) { + header > nav a { + border: none; + padding: 0; + text-decoration: underline; + line-height: 1; + } +} + +/* Consolidate box styling */ +aside, details, pre, progress { + background-color: var(--accent-bg); + border: 1px solid var(--border); + border-radius: var(--standard-border-radius); + margin-bottom: 1rem; +} + +aside { + font-size: 1rem; + width: 30%; + padding: 0 15px; + margin-inline-start: 15px; + float: right; +} +*[dir="rtl"] aside { + float: left; +} + +/* Make aside full-width on mobile */ +@media only screen and (max-width: 720px) { + aside { + width: 100%; + float: none; + margin-inline-start: 0; + } +} + +article, fieldset, dialog { + border: 1px solid var(--border); + padding: 1rem; + border-radius: var(--standard-border-radius); + margin-bottom: 1rem; +} + +article h2:first-child, +section h2:first-child { + margin-top: 1rem; +} + +section { + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + padding: 2rem 1rem; + margin: 3rem 0; +} + +/* Don't double separators when chaining sections */ +section + section, +sectionmd:first-child { + border-top: 0; + padding-top: 0; +} + +section:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +details { + padding: 0.7rem 1rem; +} + +summary { + cursor: pointer; + font-weight: bold; + padding: 0.7rem 1rem; + margin: -0.7rem -1rem; + word-break: break-all; +} + +details[open] > summary + * { + margin-top: 0; +} + +details[open] > summary { + margin-bottom: 0.5rem; +} + +details[open] > :last-child { + margin-bottom: 0; +} + +/* Format tables */ +table { + border-collapse: collapse; + margin: 1.5rem 0; +} + +figure > table { + width: max-content; +} + +td, +th { + border: 1px solid var(--border); + text-align: start; + padding: 0.5rem; +} + +th { + background-color: var(--accent-bg); + font-weight: bold; +} + +tr:nth-child(even) { + /* Set every other cell slightly darker. Improves readability. */ + background-color: var(--accent-bg); +} + +table caption { + font-weight: bold; + margin-bottom: 0.5rem; +} + +/* Misc body elements */ +hr { + border: none; + height: 1px; + background: var(--border); + margin: 1rem auto; +} + +mark { + padding: 2px 5px; + border-radius: var(--standard-border-radius); + background-color: var(--marked); + color: black; +} + +mark a { + color: #0d47a1; +} + +img, +video { + max-width: 100%; + height: auto; + border-radius: var(--standard-border-radius); +} + +figure { + margin: 0; + display: block; + overflow-x: auto; +} + +figure > img, +figure > picture > img { + display: block; + margin-inline: auto; +} + +figcaption { + text-align: center; + font-size: 0.9rem; + color: var(--text-light); + margin-block: 1rem; +} + +blockquote { + margin-inline-start: 2rem; + margin-inline-end: 0; + margin-block: 2rem; + padding: 0.4rem 0.8rem; + border-inline-start: 0.35rem solid var(--accent); + color: var(--text-light); + font-style: italic; +} + +cite { + font-size: 0.9rem; + color: var(--text-light); + font-style: normal; +} + +dt { + color: var(--text-light); +} + +/* Use mono font for code elements */ +code, +pre, +pre span, +kbd, +samp { + font-family: var(--mono-font); + color: var(--code); +} + +kbd { + color: var(--preformatted); + border: 1px solid var(--preformatted); + border-bottom: 3px solid var(--preformatted); + border-radius: var(--standard-border-radius); + padding: 0.1rem 0.4rem; +} + +pre { + padding: 1rem 1.4rem; + max-width: 100%; + overflow: auto; + color: var(--preformatted); +} + +/* Fix embedded code within pre */ +pre code { + color: var(--preformatted); + background: none; + margin: 0; + padding: 0; +} + +dialog { + max-width: 40rem; + margin: auto; +} + +dialog::backdrop { + background-color: var(--bg); + opacity: 0.8; +} + +@media only screen and (max-width: 720px) { + dialog { + max-width: 100%; + margin: auto 1em; + } +} + +/* Superscript & Subscript */ +/* Prevent scripts from affecting line-height. */ +sup, sub { + vertical-align: baseline; + position: relative; +} + +sup { + top: -0.4em; +} + +sub { + top: 0.3em; +} + diff --git a/faq b/faq @@ -0,0 +1,25 @@ +#! /bin/sh + +. ./common + +save_md() { + sed -E "/^#/ i [retour en haut](#)" | + sed '0,/retour en haut/ { /retour en haut/ d }' | + lowdown >> "$the/$1" +} + +grep -E "^##+" "$FILE" > $the/titles +< $the/titles tr -d 'àéùôêè/?#' | + tr -s ' ' | + tr ' [A-Z]' '-[a-z]' | + sed -E 's/^-/(#/;s/$/)/' > $the/slugs +sed -E 's/#/ /g;' $the/titles | + sed -E 's/ (\w)/[\1/;s/$/]/' | + sed -E 's/([^* ])/* \1/' | + paste - $the/slugs | + sed '$ a - - -' | + save_md tdm + +. "$@" + +layout diff --git a/layouts/html b/layouts/html @@ -0,0 +1,26 @@ +layout() { +<<@@ cat +<!DOCTYPE html> +<html lang="fr"> +<head> + <title>${title?La page dont le chemin s'affiche au dessus nécessite un titre}</title> + ${STYLE:+<link rel="stylesheet" href=\"$STYLE\" />} + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1.0"> + <meta name="description" content="$description" /> + <meta name="author" content="$author" /> + <link rel="icon" href="/favicon.png" /> +</head> +<header> +<h1>$title</h1> +<p>$subtitle</p> +</header> +<body> + <main> + $(the tdm) + $(the main) + </main> +</body> +</html> +@@ +} diff --git a/makefile b/makefile @@ -0,0 +1,29 @@ +.DELETE_ON_ERROR: + +sources != find contents -type f -name '*.sh' +annexes != find contents -type f -not -name '*.sh' + +pageshtml = ${sources:contents/%.sh=public/%.html} +annexescibles = ${annexes:contents/%=public/%} + +all: exec ${pageshtml} ${annexescibles} + +clean:; rm -r public/* + +exec:; chmod +x ${sources} + +deploy:; rsync -hvr public/* pi@xr:~/bebou.netlib.re/ + +test : all + busybox httpd -p 1912 -h public + @echo http://localhost:1912 +stop : + ps -aux | grep 'busybox httpd .*-p.*1912' | grep -v 'grep' | awk '{print $$2}' | xargs kill + +public/%.html : contents/%.sh page faq layouts/html + mkdir -p $(shell dirname $@); $< > $@ + +public/% : contents/% + mkdir -p $(shell dirname $@); cp $< $@ + +.PHONY: all clean exec diff --git a/page b/page @@ -0,0 +1,35 @@ +#! /bin/sh + +STYLE=/style.css + +the=$(mktemp -d) +trap "rm -rf $the" EXIT +FILE="$1" + +save_md() lowdown >> "$the/$1" + +alias sectionmd:='<<\endsection save_md' + +alias title:="title" +title() title="$*" + +alias subtitle:="subtitle" +subtitle() subtitle="$*" +alias author:="author" +author() author="$*" + +alias description:="description" +description() description="$*" + +the() { + cd $the + cat "$@" +} + +. layouts/html + +[ "$1" ] || set - + +. "$@" + +layout