feat: massive overhaul

Signed-off-by: Louis Vallat <contact@louis-vallat.fr>
This commit is contained in:
Louis Vallat 2024-04-12 17:45:33 +02:00
parent 24692aa4ab
commit c70ae62b47
Signed by: louis
SSH Key Fingerprint: SHA256:0iPwDU/PZtEy/K13Oar4TzmcunmI9H5U9IsOR3jyT/Q
40 changed files with 197 additions and 1233 deletions

5
.gitignore vendored
View File

@ -1,4 +1,3 @@
dst/ .idea/
.vscode/ .vscode/
node_modules/ public
package-lock.json

View File

@ -1,48 +0,0 @@
image: docker:latest
services:
- docker:dind
stages:
- release
- deploy
# Disable the Gradle daemon for Continuous Integration servers as correctness
# is usually a priority over speed in CI environments. Using a fresh
# runtime for each build is more reliable since the runtime is completely
# isolated from any previous builds.
variables:
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
CONTAINER_BRANCH_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
release:
stage: release
script:
- sed -i "s/__VERSION__/${CI_COMMIT_SHORT_SHA}/" src/_footer.html
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build . -t $CONTAINER_BRANCH_IMAGE
- docker push $CONTAINER_BRANCH_IMAGE
except:
- master
release-master:
stage: release
script:
- sed -i "s/__VERSION__/${CI_COMMIT_SHORT_SHA}/" src/_footer.html
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build . -t $CONTAINER_RELEASE_IMAGE
- docker push $CONTAINER_RELEASE_IMAGE
only:
- master
deploy:
stage: deploy
before_script:
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
script:
- ssh -o StrictHostKeyChecking=no -p $SSH_PORT $SSH_DESTINATION "cd $PATH_TO_APPLICATION; $UPGRADE_COMMAND;"

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "themes/terminimal"]
path = themes/terminimal
url = https://github.com/pawroman/zola-theme-terminimal.git

View File

@ -1,16 +0,0 @@
FROM alpine AS builder
WORKDIR /root
COPY . .
RUN apk --no-cache add gcc git make musl-dev npm && \
git clone -b VERSION_0_10_0 https://github.com/kristapsdz/lowdown.git && \
cd lowdown && ./configure && make && make regress && make install && cd .. && \
npm install && \
chmod u+x ./ssg5 && mkdir -p dst && \
./ssg5 ./src/ ./dst/ "Le blog de Louis Vallat" "https://blog.louis-vallat.xyz" && rm dst/.files
FROM nginx
COPY --from=builder /root/dst /usr/share/nginx/html

View File

@ -1,17 +1,9 @@
# Blog # Blog
> This is the source code from [this live website](https://blog.louis-vallat.xyz). > This is the source code from [this live website](https://blog.louis-vallat.fr).
## Todo Build it using [Zola](https://www.getzola.org):
- [ ] Add dark and light theme switch and colors ```bash
zola build
## Sources ```
- [SSG](https://www.romanzolotarev.com/ssg.html)
- [Wolfgang's video](https://youtu.be/N_ttw2Dihn8): `Fast & Minimal Blog Using POSIX Shell and Markdown`
## Licenses
The fonts provided in this repository are licensed under the [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL).
The code, if not stated otherwise, is licensed under the [GPLv3 License](LICENSE).

37
config.toml Normal file
View File

@ -0,0 +1,37 @@
base_url = "https://blog.louis-vallat.fr"
title = "Le blog de Louis"
description = "Le blog très simple d'un développeur très compliqué."
theme = "terminimal"
compile_sass = true
default_language = "fr"
generate_feed = true
feed_filename = "atom.xml"
taxonomies = [
{name = "tags"},
{name = "categories"}
]
[markdown]
highlight_code = true
[extra]
logo_text = "Le blog de Louis"
author = "Louis Vallat"
page_titles = "combined"
enable_post_view_navigation = true
post_view_navigation_prompt = "En savoir plus"
menu_items = [
{name = "blog", url = "$BASE_URL"},
{name = "tags", url = "$BASE_URL/tags"},
{name = "archive", url = "$BASE_URL/archive"},
{name = "portfolio", url = "https://louis-vallat.fr"},
{name = "gitlab", url = "https://gitlab.com/lovallat", newtab = true},
]
favicon = "/favicon.png"
favicon_mimetype = "image/png"

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,12 +1,21 @@
# Git Workflow +++
title = "Git Workflow"
description = "Git est un outil de versionnage très utile, voici comment je m'en sers."
date = 2021-05-10
[taxonomies]
tags = ["git", "workflow"]
+++
Git est un outil de versionnage très utile, voici comment je m'en sers. Git est un outil de versionnage très utile, voici comment je m'en sers.
<!-- more -->
> À noter que ce guide est destiné aux personnes qui ont déjà utilisé git auparavant, > À noter que ce guide est destiné aux personnes qui ont déjà utilisé git auparavant,
> il ne s'agit pas ici d'un tutoriel pour débutants (bien que je sois ici assez > il ne s'agit pas ici d'un tutoriel pour débutants (bien que je sois ici assez
> exhaustif dans mes explications). > exhaustif dans mes explications).
[TL;DR](#TL;DR) [TL;DR](#tldr)
## Les fondations ## Les fondations
@ -21,7 +30,7 @@ par exemple, aka. vous avez committé et quelqu'un d'autre aussi en même temps)
Nous allons donc demander à git de ne pull que lorsque l'historique est linéaire Nous allons donc demander à git de ne pull que lorsque l'historique est linéaire
(fast-forward), et sinon de faire automatiquement un rebase : (fast-forward), et sinon de faire automatiquement un rebase :
```git ```bash
git config pull.rebase true git config pull.rebase true
``` ```
@ -33,7 +42,7 @@ git. Voici un alias, à placer dans votre `~/.gitconfig`, qui vous permet d'affi
un graphique de vos branches et vos commits de manière plus visuelle que le `git log` un graphique de vos branches et vos commits de manière plus visuelle que le `git log`
de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343)) : de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343)) :
```git ```conf
[alias] [alias]
lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
@ -43,13 +52,13 @@ de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343))
Si vous n'avez pas besoin d'autant de décorations, ou que vous ne pouvez pas définir Si vous n'avez pas besoin d'autant de décorations, ou que vous ne pouvez pas définir
d'alias, cette commande fait quelque chose d'assez similaire : d'alias, cette commande fait quelque chose d'assez similaire :
```git ```bash
git log --all --decorate --oneline --graph git log --all --decorate --oneline --graph
``` ```
Pour avoir des messages de commits unifiés et cohérents avec de jolis alias : Pour avoir des messages de commits unifiés et cohérents avec de jolis alias :
```git ```conf
fix = "!f() { git commit -sm \"fix: $1\"; }; f" fix = "!f() { git commit -sm \"fix: $1\"; }; f"
feat = "!f() { git commit -sm \"feat: $1\"; }; f" feat = "!f() { git commit -sm \"feat: $1\"; }; f"
misc = "!f() { git commit -sm \"misc: $1\"; }; f" misc = "!f() { git commit -sm \"misc: $1\"; }; f"
@ -85,19 +94,19 @@ stabilité par exemple, ou qui n'est pas prête pour la mise en production.
Pour créer une branche fille à partir de la branche sur laquelle vous êtes : Pour créer une branche fille à partir de la branche sur laquelle vous êtes :
```git ```bash
git checkout -b nom_de_la_nouvelle_branche git checkout -b nom_de_la_nouvelle_branche
``` ```
Et pour basculer de branche en branche : Et pour basculer de branche en branche :
```git ```bash
git checkout nom_de_la_branche git checkout nom_de_la_branche
``` ```
Si vous ne savez pas sur quelle branche vous vous trouvez, vous pouvez faire : Si vous ne savez pas sur quelle branche vous vous trouvez, vous pouvez faire :
```git ```bash
git branch git branch
``` ```
@ -120,7 +129,7 @@ notre branche de travail, par exemple la résolution d'un bogue handicapant.
Un `rebase` va faire changer votre commit de départ et l'échanger avec un autre. Un `rebase` va faire changer votre commit de départ et l'échanger avec un autre.
Prenons le schéma suivant : Prenons le schéma suivant :
![Schéma d'un rebase](assets/images/git_flow/git_rebase.svg) ![Schéma d'un rebase](images/git_rebase.svg)
Nous avons ici la branche `new_feature` qui part d'un commit de master. Disons Nous avons ici la branche `new_feature` qui part d'un commit de master. Disons
que vous êtes en train de développer une fonctionnalité importante, et qu'un bug que vous êtes en train de développer une fonctionnalité importante, et qu'un bug
@ -129,7 +138,7 @@ de votre branche, et qu'il est disponible sur `master`. Pour le récupérer sur
branche, vous n'avez qu'à `rebase` votre branche sur la dernière version de `master`. branche, vous n'avez qu'à `rebase` votre branche sur la dernière version de `master`.
La commande pour faire ceci serait alors : La commande pour faire ceci serait alors :
```git ```bash
git rebase nom_de_branche_parente git rebase nom_de_branche_parente
``` ```
@ -173,7 +182,7 @@ forcément toutes en tête.
Pour fusionner une branche dans celle sur laquelle vous vous trouvez : Pour fusionner une branche dans celle sur laquelle vous vous trouvez :
```git ```bash
git merge nom_de_la_branche_à_fusionner_dans_celle_sur_laquelle_vous_êtes git merge nom_de_la_branche_à_fusionner_dans_celle_sur_laquelle_vous_êtes
``` ```
@ -188,7 +197,7 @@ temps de lui dire au revoir !
Commençons d'abord par la supprimer sur le repo distant : Commençons d'abord par la supprimer sur le repo distant :
```git ```bash
git push -d origin nom_de_branche git push -d origin nom_de_branche
``` ```
@ -200,7 +209,7 @@ références locales à des branches fantômes distantes avec la commande `git f
Enfin, il est temps de supprimer votre copie locale de cette branche : Enfin, il est temps de supprimer votre copie locale de cette branche :
```git ```bash
git branch -d nom_de_branche git branch -d nom_de_branche
``` ```
@ -210,12 +219,12 @@ possible de forcer la suppression d'une branche locale non fusionnée avec l'opt
"Don't cry because it's over, smile because it happened." - *Dr. Seuss* "Don't cry because it's over, smile because it happened." - *Dr. Seuss*
## TL;DR ## TL;DR {#tldr}
On commence par demander à git de ne pull que s'il n'y a pas de divergence avec On commence par demander à git de ne pull que s'il n'y a pas de divergence avec
la version distante de notre branche, et sinon, de rebase notre branche automatiquement : la version distante de notre branche, et sinon, de rebase notre branche automatiquement :
```git ```bash
git config pull.rebase true git config pull.rebase true
``` ```

View File

Before

Width:  |  Height:  |  Size: 553 KiB

After

Width:  |  Height:  |  Size: 553 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

@ -1,4 +1,11 @@
# Robot timide le robot qui fuit la lumière +++
title = "Robot timide le robot qui fuit la lumière"
description = "Création d'un robot timide pour un projet scolaire."
date = 2022-05-19
[taxonomies]
tags = ["maker", "fablab", "robotique"]
+++
Création d'un robot timide pour un projet scolaire. Création d'un robot timide pour un projet scolaire.
@ -9,7 +16,9 @@ en filière Informatique, un robot timide a vu le jour au sein dEirlab.
Cet article est un guide pour expliquer comment reproduire notre travail, dans Cet article est un guide pour expliquer comment reproduire notre travail, dans
le plus pur esprit Maker. le plus pur esprit Maker.
![Une vue de 3/4 face du robot finalisé](./assets/images/robot-timide/robot_timide_header.jpg) <!-- more -->
![Une vue de 3/4 face du robot finalisé](images/robot_timide_header.jpg)
## Concept de base ## Concept de base
@ -20,7 +29,7 @@ mode est aussi disponible, permettant non pas daller vers lendroit le plus
sombre, mais à linverse de fuir lendroit le plus lumineux, pour avoir deux sombre, mais à linverse de fuir lendroit le plus lumineux, pour avoir deux
manières de voir le problème. manières de voir le problème.
![Une vue de face du robot timide dans sa forme finale.](./assets/images/robot-timide/robot_timide_enseirb.jpg) ![Une vue de face du robot timide dans sa forme finale.](images/robot_timide_enseirb.jpg)
## Du matériel ## Du matériel
@ -55,7 +64,7 @@ percée pour accueillir les différents modules du robot. Les éléments les plu
légers ou risquant moins de se détacher sont fixés par un simple système de légers ou risquant moins de se détacher sont fixés par un simple système de
mâchoire, prenant en étau la plaque de bois dans une forme de U. mâchoire, prenant en étau la plaque de bois dans une forme de U.
![La forme de U pour prendre la plaque de base en mâchoire et tenir un capteur par pression.](./assets/images/robot-timide/u_shape.jpg) ![La forme de U pour prendre la plaque de base en mâchoire et tenir un capteur par pression.](images/u_shape.jpg)
### Alimentation ### Alimentation
@ -69,7 +78,7 @@ et prend donc des tensions entre 5 et 9 Volts.
Dans ce projet, nous utilisons une pile 9 Volts rechargeable dans un boîtier Dans ce projet, nous utilisons une pile 9 Volts rechargeable dans un boîtier
vissé au châssis. vissé au châssis.
![Une vue de la pile 9 Volts que nous utilisons, rechargeable en micro USB.](./assets/images/robot-timide/pile9v.jpg) ![Une vue de la pile 9 Volts que nous utilisons, rechargeable en micro USB.](images/pile9v.jpg)
### Moteurs ### Moteurs
@ -92,11 +101,11 @@ aussi appelé breadboard. Dans un second temps et pour éviter toute déconnexio
de câbles liée à une potentielle accélération violente, nous avons fait un PCB, de câbles liée à une potentielle accélération violente, nous avons fait un PCB,
soudé manuellement sur une carte de prototypage prévu à cet effet. soudé manuellement sur une carte de prototypage prévu à cet effet.
![Une vue du dessus de notre PCB fait main.](./assets/images/robot-timide/pcb_irl.jpg) ![Une vue du dessus de notre PCB fait main.](images/pcb_irl.jpg)
Au niveau électronique, le PCB est routé de la manière suivante : Au niveau électronique, le PCB est routé de la manière suivante :
![Le routage du PCB sur une carte de protypage de type Breadboard.](./assets/images/robot-timide/pcb_fritzing.png) ![Le routage du PCB sur une carte de protypage de type Breadboard.](images/pcb_fritzing.png)
Les différents composants viennent se connecter sur le PCB au moyen de Les différents composants viennent se connecter sur le PCB au moyen de
différentes broches femelles, pour que ce dernier puisse être détaché et modifié différentes broches femelles, pour que ce dernier puisse être détaché et modifié

4
content/_index.md Normal file
View File

@ -0,0 +1,4 @@
+++
paginate_by=5
sort_by = "date"
+++

6
content/pages/archive.md Normal file
View File

@ -0,0 +1,6 @@
+++
title = "Articles archivés"
path = "archive"
+++
N/A

View File

@ -1,20 +0,0 @@
const Prism = require('prismjs');
const fs = require('fs');
const cheerio = require('cheerio');
const loadLanguages = require('prismjs/components/');
const fsc = require('fs-cheerio');
loadLanguages([
'rust', 'py', 'cpp', 'c', 'git'
]);
const file = process.argv[2];
fsc.readFile(file).then(function ($, err) {
$('*[class^="language-"]').replaceWith(function () {
return $(this).html(Prism.highlight(
$(this).text(),
Prism.languages[$(this).attr('class').split('-')[1]])
);
});
fsc.writeFile(file, $);
});

View File

@ -1,7 +0,0 @@
{
"dependencies": {
"cheerio": "^1.0.0-rc.10",
"fs-cheerio": "^3.0.0",
"prismjs": "^1.26.0"
}
}

View File

@ -1,11 +0,0 @@
<footer>
<p>
License: <a href="https://choosealicense.com/licenses/gpl-3.0/" target="_blank">G.P.L. v3</a><br>
Made with ❤ and <a href="https://gitlab.com/lovallat/blog" target="_blank">Open Source Software</a> &mdash;
Version: <code>__VERSION__</code>
</p>
</footer>
</body>
</html>

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<!--
PAGE CODED BY :
__ _____ __ __ ______ ____ __ __ ______ __ __ ______ ______
/\ \ /\ __`\/\ \/\ \/\__ _\/\ _`\ /\ \/\ \/\ _ \/\ \ /\ \ /\ _ \/\__ _\
\ \ \ \ \ \/\ \ \ \ \ \/_/\ \/\ \,\L\_\ \ \ \ \ \ \ \L\ \ \ \ \ \ \ \ \ \L\ \/_/\ \/
\ \ \ __\ \ \ \ \ \ \ \ \ \ \ \ \/_\__ \ \ \ \ \ \ \ __ \ \ \ __\ \ \ __\ \ __ \ \ \ \
\ \ \L\ \\ \ \_\ \ \ \_\ \ \_\ \__/\ \L\ \ \ \ \_/ \ \ \/\ \ \ \L\ \\ \ \L\ \\ \ \/\ \ \ \ \
\ \____/ \ \_____\ \_____\/\_____\ `\____\ \ `\___/\ \_\ \_\ \____/ \ \____/ \ \_\ \_\ \ \_\
\/___/ \/_____/\/_____/\/_____/\/_____/ `\/__/ \/_/\/_/\/___/ \/___/ \/_/\/_/ \/_/
-->
<html lang="fr">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/assets/images/icon.png">
<!-- SEO -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="image" content="https://blog.louis-vallat.fr/assets/images/icon.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="">
<meta name="twitter:description" content="">
<meta name="twitter:site" content="@VallatLouis">
<meta name="twitter:creator" content="@VallatLouis">
<meta name="twitter:image:src" content="https://blog.louis-vallat.fr/assets/images/icon.png">
<meta name="og:title" content="">
<meta name="og:description" content="">
<meta name="og:image" content="https://blog.louis-vallat.fr/assets/images/icon.png">
<meta name="og:url" content="https://blog.louis-vallat.fr/">
<meta name="og:site_name" content="Le blog de Louis Vallat">
<meta name="og:locale" content="fr_FR">
<meta name="og:type" content="website">
<!-- Reset CSS -->
<link rel="stylesheet" href="/assets/css/reset.css">
<!-- Main CSS -->
<link rel="stylesheet" href="/assets/css/style.css">
<title></title>
</head>
<body>
<nav>
<a href="/" id="home_link">accueil</a>
<div class="filler"></div>
<a href="mailto:contact@louis-vallat.fr" target="_blank">contact</a>
<a href="https://gitlab.com/lovallat" target="_blank">gitlab</a>
<a href="https://louis-vallat.fr" target="_blank">site</a>
<a href="https://twitter.com/VallatLouis" target="_blank">twitter</a>
</nav>

View File

@ -1,73 +0,0 @@
# AoC 2021 Jour 0: Commencement
Le début d'une grande aventure !
Cette année, j'ai découvert l'[Advent Of Code](https://adventofcode.com/).
Ce site géré de manière bénévole par [Eric Wastl](http://was.tl/) sur son temps
libre met à disposition des puzzles sur un thème différent d'une année sur
l'autre. Ces petits puzzles sont à résoudre à l'aide d'un programme informatique
ou d'un algorithme que l'on est sensé imaginer et mettre en oeuvre, pour, à partir
d'un fichier d'entrée qui nous est propre, donner un résultat à troquer sur le site
du challenge en échange d'une étoile. Le langage et la manière d'obtenir le
résultat sont libres, mais faire les défis "à la main" est rarement possible
(en tous cas en temps humain). Pendant tout le mois de décembre, un défi est publié
chaque jour du 1er au 25. Ils sont systématiquement découpés en 2 parties, la
seconde n'étant accessible qu'une fois la première résolue.
En échange d'une réponse valide au problème, une étoile nous est donnée, pour avoir
un total de 50 étoiles par an.
Il n'y a pas de pénalité si le puzzle n'est pas résolu dans la journée, mais un
*leaderboard* est disponible, et les 100 premiers à résoudre les puzzles obtiennent
des points sur leur profil. C'est comme cela que j'ai découvert qu'il existait
une discipline de [Programmation Compétitive](https://en.wikipedia.org/wiki/Competitive_programming).
Cette année, le thème c'était la quête de la clé du traîneau du Père Noël au fond
d'une crevasse sous-marine, armé d'un petit sous-marin, et rencontrant au passage
une faune et flore locale diverse.
![Vue de l'intérieur d'un sous-marin d'exploration. Photo par Michael Mrozek pour Unsplash.](./assets/images/aoc-2021-day-0/michal-mrozek-BEUd36I1-f8-unsplash.jpg)
J'ai déjà résolu les 25 jours et obtenu mes 50 étoiles, je souhaite maintenant
raconter ici mon aventure, jour après jour, et les algorithmes que j'ai mis en
place.
## Pourquoi
J'ai choisi de participer à cet évènement pour différentes raisons :
- j'aime les défis, je trouve amusant et plaisant d'avoir un but à atteindre en code
- je voulais m'exercer, j'aime travailler sur des projets sur mon temps libre
## Comment
Mes algorithmes sont implémentés en [Rust](https://www.rust-lang.org/), un langage
que j'aime beaucoup, pour différentes raisons qui sont hors sujet par rapport à
cette série d'articles. Un article spécifique sur ce qui me plaît dans ce
langage de programmation arrivera peut-être plus tard.
Mon but était de résoudre les puzzles en temps limité, si possible en moins d'une
journée, et que le programme final donne sa réponse le plus rapidement possible.
Si après 3 jours à travailler sur un puzzle, il donne une réponse en 300ms, cela
me convient. **Le but n'étant pas ici de faire des algorithmes optimaux.**
Chaque jour a été mis à disposition sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021).
## Beurk
Vous trouvez le code illisible ? C'est **normal**.
J'ai tendance dans mes programmes à avoir de mauvaises habitudes, *hostiles* à
la relecture de code. J'ai tendance à utiliser par exemple des variables dont le
nom se résume à une unique lettre. J'ai aussi tendance à vouloir produire un
fichier source le plus court possible (tout en respectant les standards du langage
utilisé). Le code fourni est alors un bloc compact **difficile à lire**.
J'ai donc décidé de me faire subir le même sort que celui réservé à mes collègues
lorsqu'il doivent relire mon code, afin de me faire **perdre** ces mauvaises
habitudes. (*spoiler: c'était efficace*)
## À suivre
Seront publiés sur ce blog différents articles retraçant et expliquant jour après
jour ce que mes algorithmes et programmes font pour résoudre les différents défis.

View File

@ -1,112 +0,0 @@
# AoC 2021 Jour 1: Sonar Sweep
À la recherche de la clef du traîneau à l'aide d'un sonar.
Le défi peut être trouvé [ici](https://adventofcode.com/2021/day/1), et le code
lié est sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021/-/tree/master/day1).
## Consigne du défi
Le premier puzzle est assez simple, et peut se résumer en "compter les variations
dans un fichier d'entrée".
## Lecture du fichier d'entrée
Pour lire le fichier, rien de plus simple. L'entrée étant une simple liste d'entiers
séparés par des retours à la ligne, il est assez facile de le transformer en
tableau d'entiers.
En code, cela se traduit par :
```rust
fn parse_input(s: &str) -> Vec<i32> {
return s.split("\n").into_iter().filter_map(|f| f.parse::<i32>().ok()).collect::<Vec<i32>>();
}
```
Maintenant que j'ai acquis un peu plus d'expérience en Rust, je le ferais d'une
manière assez différente :
- tout d'abord, `s.split("\n").into_iter()` peut être remplacé par `s.lines()`,
plus robuste et [System Agnostic](https://en.wikipedia.org/wiki/Agnostic_(data)),
les systèmes Windows utilisant une fin de ligne [différente](https://en.wikipedia.org/wiki/Newline#In_programming_languages).
- ensuite, utiliser des `u16`, soit des entiers non signés et encodés sur 16 bits.
Ici utiliser 32 bits signés pour stocker des valeurs relativement faibles (dans ce
jeu de données) et strictement positives n'est pas optimal.
## Première partie
La première partie nécessite de compter les augmentations de nombres un-à-un.
Nous avons donc un tableau (vecteur) d'entiers, alors il suffira simplement de le
parcourir, et si l'entier actuel est plus grand que le précédent, alors on retient
qu'on a vu une augmentation supplémentaire.
Le code de cette partie est le suivant :
```rust
fn get_increase_nb(e: &Vec<i32>) -> i32 {
let mut n = 0;
for i in 1..e.len() {
n += if e[i] > e[i - 1] { 1 } else { 0 };
}
return n;
}
```
On a donc un entier `n` qui va retenir le nombre d'augmentations observées,
et pour tous les entiers du tableau, de la case 1 (les tableaux en informatique
débutent à la case 0), à la case `taille` (non incluse, toujours parce que les
tableaux commencent à 0). Si le nombre actuel est plus grand que le précédent,
alors `n` augmente de 1.
## Deuxième partie
Pour cette deuxième partie, le concept est le même mais cette fois on prend les
entiers d'entrée par groupe de 3 et on compte les augmentations entre chaque
sommes d'entiers.
On transforme donc le tableau d'entiers en un second tableau correspondant aux
sommes de 3 entiers selon une fenêtre flottante.
```rust
fn get_sums(v: &Vec<i32>) -> BTreeMap<usize, i32> {
let mut sums = BTreeMap::new();
for i in 0..(v.len() - 2) {
sums.insert(i, v[i] + v[i + 1] + v[i + 2]);
}
return sums;
}
```
Ici, une [BTreeMap](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html)
est **totalement** inutile. Je ne sais pas trop pourquoi j'ai choisi ce type de
structure de données. Surtout qu'après, cette Map est convertie en simple tableau
en ignorant totalement la clef :
```rust
let map = get_sums(&vec_in).iter().map(|e| e.1.to_owned()).collect::<Vec<i32>>();
```
Ici il serait *beaucoup plus intéressant* d'utiliser un simple tableau, et à chaque
nouvelle somme on peut l'envoyer sur le dessus du tableau (de sommes) d'entiers.
Ce tableau enfin obtenu sera alors envoyé à la même méthode que pour la partie une
pour en obtenir le nombre d'augmentations.
Évidemment il serait aussi possible de simplement compter les augmentations en même
temps que l'on parcourt les tableaux, pour éviter de multiples passages.
## Conclusion
Ce puzzle était loin d'être compliqué. J'ai passé un peu de temps sur la
compréhension du système de sommes de la partie 2, ne sachant que faire dans le
cas où il n'y avait plus assez d'entiers pour faire une somme.
Après un peu de temps et d'essais, j'ai pu m'en sortir sans difficulté.
Concernant la qualité du code, je me demande bien ce que j'ai pu penser sur le
moment pour choisir de stocker les sommes dans une map à arbre binaire...
> À suivre

View File

@ -1,127 +0,0 @@
# AoC 2021 Jour 2: Dive
Comment piloter le sous-marin des elfes ?
Le défi peut être trouvé [ici](https://adventofcode.com/2021/day/2), et le code
lié est sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021/-/tree/master/day2).
## Consigne du défi
Le sous-marin peut prendre 3 types de commandes :
- `forward X`
- `down X`
- `up X`
Le fichier d'entrée est une suite de ces ordres, que l'on doit interpréter pour
trouver la position du sous-marin dans l'espace (horizontalement et verticalement).
## Lecture du fichier d'entrée
La lecture du fichier d'entrée se fait à nouveau dans une seule fonction :
```rust
fn parse_input(s: &str) -> Vec<(String, i32)> {
let mut r:Vec<(String, i32)> = vec![];
let v = s.split("\n").into_iter();
for e in v {
let p = e.split_whitespace().collect::<Vec<&str>>();
if p.len() == 2 && p[1].parse::<i32>().is_ok() {
r.push((p[0].to_owned(), p[1].parse::<i32>().unwrap()));
}
}
return r;
}
```
Ici, on va tout simplement transformer une ligne en tuple `(String, i32)`, et
de transformer le fichier d'entrée en un tableau `Vec` de ces ordres. On découpe
donc une ligne `ordre X` en fonction d'un espace, et si on a bien 2 éléments et
que le deuxième peut être converti en entier signé stocké sur 32 bits, alors on
l'ajoute à notre tableau d'instructions.
On pourrait ici utiliser une énumeration telle que `Direction::Forward`,
`Direction::Up` et `Direction::Down` pour éviter de stocker les chaînes de
caractère en entier dans le tableau. On pourrait aussi utiliser des entiers stockés
sur moins de bits, étant relativement petits.
## Première partie
Pour la première partie, l'interprétation des instructions est la suivante :
- `forward X` qui permet de faire avancer le sous-marin de `X` unités horizontalement
- `down X` fait plonger le sous-marin et donc augmente sa profondeur de `X` unités
- `up X` fait remonter à l'inverse le sous-marin vers la surface
La réponse à la première partie se trouve en multipliant la position horizontale
et verticale. Pour cela, on va faire un `fold` sur le tableau pour calculer toutes
les instructions à la suite et donnant le résultat en une seule ligne :
```rust
fn get_horizontal_depth(v: Vec<(String, i32)>) -> (i32, i32) {
return v.into_iter().fold((0, 0), |acc, e| compute_horizontal_depth(e, acc));
}
```
Chaque ligne est traitée individuellement pour interprétation, en donnat en valeur
de retour les nouvelles positions du sous-marin après application de l'instruction
sur la position donnée en entrée :
```rust
fn compute_horizontal_depth(t: (String, i32), a: (i32, i32)) -> (i32, i32) {
return match t.0.as_str() {
"up" => (a.0, a.1 - t.1),
"down" => (a.0, a.1 + t.1),
"forward" => (a.0 + t.1, a.1),
_ => a
};
}
```
Ce traitement est donc très simple. Pour chaque instruction et en partant d'une
position `(0, 0)`, on va appliquer les règles les unes après les autres en mettant
à jour la position à chaque fois. Cela se fait en temps linéaire sans calcul autre
que des additions.
## Deuxième partie
Pour la deuxième partie, le traitement est assez similaire, mais on va calculer
une composante supplémentaire : la *visée*.
Les instructions sont désormais à interpréter de cette manière :
- `forward X` permet deux choses :
- augmente la position horizontale de `X` unités
- augmente la profondeur par la *visée* multipliée par `X`
- `down X` augmente la *visée* de `X` unités
- `up X` à l'inverse diminue la *visée* de `X` unités
Le concept pour calculer est le même, on applique les instructions l'une après
l'autre en partant de `(0, 0, 0)`.
```rust
fn compute_horizontal_depth_aim(t: (String, i32), a: (i32, i32, i32)) -> (i32, i32, i32) {
return match t.0.as_str() {
"up" => (a.0, a.1, a.2 - t.1),
"down" => (a.0, a.1, a.2 + t.1),
"forward" => (a.0 + t.1, a.1 + a.2 * t.1, a.2),
_ => a
};
}
```
Enfin, la réponse à donner est la multiplication de la position horizontale et
verticale, comme pour la précédente partie.
## Conclusion
Ce défi n'était pas compliqué et ma réponse me semble adaptée. Bien sûr il y a
toujours des pistes d'améliorations, telle que le type de données stockées :
les chaînes de caractères étant moins performantes à traiter, mais aussi la taille
des entiers stockés, ou encore le fait que je ne passe pas de référence aux
fonctions, nécéssitant une copie systématique, consommant des cycles supplémentaires.
Mais globalement je suis satisfait de ma réponse à ce défi.
> À suivre

View File

@ -1,167 +0,0 @@
# AoC 2021 Jour 3: Binary Diagnostic
Grincements étranges et inquiétants dans le sous-marin.
Le défi peut être trouvé [ici](https://adventofcode.com/2021/day/3), et le code
lié est sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021/-/tree/master/day3).
## Consigne du défi
Dans ce défi, on nous apprend que le sous-marin fait des bruits *pour le moins
étranges*. Le fichier d'entrée se compose d'une longue liste de nombres binaires,
que nous devons analyser.
## Lecture du fichier d'entrée
La lecture du fichier d'entrée se fait de la manière suivante, assez simplement :
```rust
fn parse_input(s: &str) -> Vec<Vec<u32>> {
let mut r: Vec<Vec<u32>> = vec![];
for e in s.split("\n") {
if !e.is_empty() {
r.push(e.chars().filter_map(|e| e.to_digit(2)).collect());
}
}
return r;
}
```
On va donc transformer chaque ligne (par exemple `101000001100`) en un tableau
de chiffres, ayant pour valeur `0` ou `1` (dans l'exemple
`[ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 ]`). On fait cela pour chaque ligne, pour
assembler une matrice de chiffres binaires, faciles à parcourir dans toutes les
directions.
## Première partie
Pour la première partie, il va falloir créer un nombre binaire à partir des chiffres
les **plus** fréquents dans les colonnes, et un nombre binaire à partir des chiffres
les **moins** fréquents, toujours dans les colonnes. On obtiendra alors deux
nombres binaires, qui une fois convertis en base 10 et ensuite multipliés entre eux,
donne la réponse à cette première partie.
Une fonction utilitaire permet de convertir les tableaux de nombres binaires en
nombre entier non signé :
```rust
fn bit_vec_to_u32(v: &Vec<u32>, invert: bool) -> u32 {
let mut acc = 0;
for i in 0..v.len() {
acc += if invert { (v[i] + 1) % 2 } else { v[i] } << (v.len() - 1 - i);
}
return acc;
}
```
On a donc ici un accumulateur qui fait la somme des éléments tout en faisant un
décalage binaire, produisant un nombre entier en temps linéaire.
On va donc utiliser une fonction permettant d'obtenir, pour tout le fichier d'entrée,
le bit le plus présent dans la colonne `c` :
```rust
fn get_most_in_col(v: &Vec<Vec<u32>>, c: usize) -> u32 {
let mut a = (0, 0);
for r in 0..v.len() {
if v[r][c] == 0 {
a.0 += 1;
} else {
a.1 += 1;
}
}
if a.1 == a.0 { return 1; }
else { return if a.1 > a.0 { 1 } else { 0 }; }
}
```
À la fin de la fonction, on peut voir que lors d'une égalité, c'est le `1` qui est
considéré comme valeur la plus présente dans la colonne étudiée.
**Amélioration possible :** il serait potentiellement plus intéressant de faire la somme des entiers stockés
dans la colonne, et regarder si cette somme est supérieure au nombre de lignes
divisée par deux, car cela nous évite de faire des comparaisons et rend inutiles
les conditions `if` qui sont au milieu de la boucle, ralentissent fortement
le calcul et sont donc à **proscrire**.
On va donc ici calculer en un seul coup la suite de chiffres binaires les plus
représentés par colonne dans le fichier d'entrée. La valeur `gamma` sera donc la
conversion directe de ce tableau en nombre entier non signé, et la valeur `epsilon`
sera aussi une conversion, mais inversée.
```rust
fn get_gamma_epsilon(v: &Vec<Vec<u32>>) -> (u32, u32) {
let mut acc: Vec<u32> = vec![];
for c in 0..v[0].len() {
acc.push(get_most_in_col(&v, c));
}
return (bit_vec_to_u32(&acc, false), bit_vec_to_u32(&acc, true));
}
```
La multiplication de ces deux valeurs nous donne la réponse à cette première partie.
## Deuxième partie
Toujours à partir de ce fichier d'entrée, on doit alors calculer deux nouvelles
valeurs : le niveau de génération d'oxygène et le niveau de filtrage de CO2.
Pour obtenir ces deux valeurs, on joue encore sur les bits les plus et les moins
communs dans une colonne donnée mais en rajoutant cette fois un concept
d'élimination. On va donc faire ceci :
- On récupère le bit le plus (ou le moins) présent dans la colonne observée
- On ne conserve que les nombres binaires qui ont cette valeur de bit pour cette colonne
- On répète cette opération jusqu'à ce qu'il ne reste qu'une seule valeur
Ici, j'ai utilisé une approche récursive :
```rust
fn get_o2(v: &Vec<Vec<u32>>, c: usize) -> u32 {
if v.len() == 1 { return bit_vec_to_u32(&v[0], false); }
let a = get_most_in_col(&v, c);
let f = v.into_iter().filter(|e| e[c] == (a + 1) % 2)
.fold(vec![], |mut acc, e| { acc.push(e.to_owned()); return acc;});
return get_o2(&f, c + 1);
}
fn get_co2(v: &Vec<Vec<u32>>, c: usize) -> u32 {
if v.len() == 1 { return bit_vec_to_u32(&v[0], false); }
let a = get_most_in_col(&v, c);
let f = v.into_iter().filter(|e| e[c] == a)
.fold(vec![], |mut acc, e| { acc.push(e.to_owned()); return acc;});
return get_co2(&f, c + 1);
}
```
On va donc récupérer le bit le plus présent dans la colonne `c`, et conserver les
valeurs qui nous intéressent, puis rappeler la méthode sur les valeurs restantes
en avançant d'une colonne. Une fois qu'il ne reste qu'une valeur, on sait qu'on
a notre résultat, et c'est ce qui est renvoyé.
En relisant ce code, j'ai plusieurs critiques à en faire :
- J'ai visiblement inversé le calcul de CO2 et de O2. Dans le calcul pour l'oxygène,
je ne conserve que les bits les moins présents, alors que c'est les plus présents
que je devrais conserver. Cela ne pose pas de problème car je dois multiplier les
deux valeurs entre elles, et l'ordre des termes d'un produit importe peu.
- J'utilise `(a + 1) % 2` pour obtenir l'inverse d'un bit, alors qu'un simple "différent"
suffirait. Cela demande de faire des opérations coûteuses (la division est coûteuse
en cycles CPU) pour rien.
- Je fais une copie du tableau avec les éléments restants après le tri au lieu de
faire un simple `filter`. Une copie est de toutes façons nécessaire, mais filtrer
le tableau serait plus élégant.
## Conclusion
Ce défi fait monter très légèrement la difficulté et le temps de réflexion
nécessaire, mais cela reste très accessible. Encore une fois, relire son code
et se poser les bonnes questions est très important, surtout se demander si telle
ou telle copie ou opération est bien nécessaire. C'est une réflexion que je
m'appliquerai à faire plus souvent.
> À suivre

View File

@ -1,48 +0,0 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -1,218 +0,0 @@
@charset "utf-8";
/* Coded by Louis Vallat */
@font-face {
font-family: 'JetBrains Mono';
src: url("/assets/fonts/JetBrainsMono.ttf");
font-display: swap;
}
@font-face {
font-family: 'IBM Plex Mono';
src: url("/assets/fonts/IBMPlexMono.ttf");
font-display: swap;
}
@font-face {
font-family: 'Open Sans';
src: url("/assets/fonts/OpenSans-Regular.ttf");
font-display: swap;
}
:root {
--bg: #2A2D34;
--light_bg: #414652;
--fg: #fefefe;
--link: #009DDC;
--link_h: #009B72;
--link_v: #ACACDE;
}
blockquote {
border-left: 5px solid var(--light_bg);
border-radius: 5px;
margin-left: 10px;
padding-left: 7px;
}
body {
font-family: 'Open Sans', sans-serif;
background-color: var(--bg);
color: var(--fg);
margin-right: 27vw;
margin-left: 27vw;
text-align: justify;
font-size: 16px;
}
p, ul {
margin-top: 10px;
line-height: 1.55em;
}
li>ul {
margin-top: 0;
margin-left: 15px;
list-style-type: circle;
}
img {
margin-top: 15px;
margin-bottom: 5px;
width: 100%;
}
a {
color: var(--link);
}
a:visited {
color: var(--link_v);
}
a:hover {
color: var(--link_h);
}
nav {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-top: 10px;
margin-bottom: 10px;
flex-wrap: wrap;
}
.filler {
flex-grow: 2;
}
nav>a {
font-family: 'Open Sans', sans-serif;
padding: 10px;
}
nav>a:visited, nav>a:hover, nav>a {
color: var(--fg);
text-decoration: none;
}
nav>a:not(#home_link) {
margin-left: 25px;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'JetBrains Mono', sans-serif;
font-weight: bold;
margin-top: 20px;
margin-bottom: 10px;
}
ul {
list-style: disc;
list-style-position: inside;
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 1.85em;
}
h3 {
font-size: 1.65em;
}
h4 {
font-size: 1.45em;
}
h5 {
font-size: 1.35em;
}
h6 {
font-size: 1.25em;
}
b, strong {
font-weight: bold;
}
em, i {
font-style: italic;
}
footer {
font-size: 14px;
margin-bottom: 100px;
}
pre {
margin-bottom: 5px;
margin-top: 5px;
}
pre>code {
padding: 10px;
display: block;
overflow: auto;
line-height: 1.35em;
}
code {
padding: 2px 5px 2px 5px;
border-radius: 5px;
background-color: var(--light_bg);
font-family: 'IBM Plex Mono', monospace;
}
/* Code blocks colors */
.token.keyword, .token.macro {
color: #D55FDE;
}
.token.function {
color: #52ADF2;
}
.token.class-name {
color: #E5C07B;
}
.token.punctuation, .token.operator {
color: #AAB1C0;
}
.token.string {
color: #89CA78;
}
.token.number {
color: #D8985F;
}
@media screen and (max-width: 992px) {
body {
margin-right: 10vw;
margin-left: 10vw;
}
nav>a:not(#home_link) {
margin-left: 5px;
}
}
@media screen and (max-width: 768px) {
body {
margin-right: 5vw;
margin-left: 5vw;
}
nav>a:not(#home_link) {
margin-left: 0px;
}
blockquote {
margin-left: 0px;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,12 +0,0 @@
#### Articles
Un blog on ne peut plus simple.
- [Archive/Projet Makers ENSEIRB - Robot timide](./robot-timide-robot-qui-fuit-la-lumiere.html)
- Advent of Code 2021 :
- [Advent of Code 2021 Jour 3 : Binary Diagnostic](./aoc-2021-jour-3-binary-diagnostic.html) - *07/02/2021*
- [Advent of Code 2021 Jour 2 : Dive](./aoc-2021-jour-2-dive.html) - *24/01/2021*
- [Advent of Code 2021 Jour 1 : Sonar Sweep](./aoc-2021-jour-1-sonar-sweep.html) - *19/01/2021*
- [Advent of Code 2021 Jour 0 : le commencement](./aoc-2021-jour-0-commencement.html) - *17/01/2022*
- [Git workflow](./git_flow.html) - *10/05/2021*

286
ssg5
View File

@ -1,286 +0,0 @@
#!/bin/sh -e
#
# https://rgz.ee/bin/ssg5
# Copyright 2018-2019 Roman Zolotarev <hi@romanzolotarev.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
main() {
test -n "$1" || usage
test -n "$2" || usage
test -n "$3" || usage
test -n "$4" || usage
test -d "$1" || no_dir "$1"
test -d "$2" || no_dir "$2"
src=$(readlink_f "$1")
dst=$(readlink_f "$2")
IGNORE=$(
if ! test -f "$src/.ssgignore"
then
printf ' ! -path "*/.*"'
return
fi
while read -r x
do
test -n "$x" || continue
printf ' ! -path "*/%s*"' "$x"
done < "$src/.ssgignore"
)
# files
title="$3"
h_file="$src/_header.html"
f_file="$src/_footer.html"
test -f "$f_file" && FOOTER=$(cat "$f_file") && export FOOTER
test -f "$h_file" && HEADER=$(cat "$h_file") && export HEADER
list_dirs "$src" |
(cd "$src" && cpio -pdu "$dst")
fs=$(
if test -f "$dst/.files"
then list_affected_files "$src" "$dst/.files"
else list_files "$1"
fi
)
if test -n "$fs"
then
echo "$fs" | tee "$dst/.files"
if echo "$fs" | grep -q '\.md$'
then
if test -x "$(which lowdown 2> /dev/null)"
then
echo "$fs" | grep '\.md$' |
render_md_files_lowdown "$src" "$dst" "$title"
else
if test -x "$(which Markdown.pl 2> /dev/null)"
then
echo "$fs" | grep '\.md$' |
render_md_files_Markdown_pl "$src" "$dst" "$title"
else
echo "couldn't find lowdown nor Markdown.pl"
exit 3
fi
fi
fi
echo "$fs" | grep '\.html$' |
render_html_files "$src" "$dst" "$title"
echo "$fs" | grep -Ev '\.md$|\.html$' |
(cd "$src" && cpio -pu "$dst")
fi
printf '[ssg] ' >&2
print_status 'file, ' 'files, ' "$fs" >&2
# sitemap
base_url="$4"
date=$(date +%Y-%m-%d)
urls=$(list_pages "$src")
test -n "$urls" &&
render_sitemap "$urls" "$base_url" "$date" > "$dst/sitemap.xml"
print_status 'url' 'urls' "$urls" >&2
echo >&2
}
readlink_f() {
file="$1"
cd "$(dirname "$file")"
file=$(basename "$file")
while test -L "$file"
do
file=$(readlink "$file")
cd "$(dirname "$file")"
file=$(basename "$file")
done
dir=$(pwd -P)
echo "$dir/$file"
}
print_status() {
test -z "$3" && printf 'no %s' "$2" && return
echo "$3" | awk -v singular="$1" -v plural="$2" '
END {
if (NR==1) printf NR " " singular
if (NR>1) printf NR " " plural
}'
}
usage() {
echo "usage: ${0##*/} src dst title base_url" >&2
exit 1
}
no_dir() {
echo "${0##*/}: $1: No such directory" >&2
exit 2
}
list_dirs() {
cd "$1" && eval "find . -type d ! -name '.' ! -path '*/_*' $IGNORE"
}
list_files() {
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE"
}
list_dependant_files () {
e="\\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \\)"
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE $e"
}
list_newer_files() {
cd "$1" && eval "find . -type f ! -name '.' $IGNORE -newer $2"
}
has_partials() {
grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$'
}
list_affected_files() {
fs=$(list_newer_files "$1" "$2")
if echo "$fs" | has_partials
then list_dependant_files "$1"
else echo "$fs"
fi
}
render_html_files() {
while read -r f
do render_html_file "$3" < "$1/$f" > "$2/$f"
done
}
render_md_files_lowdown() {
while read -r f
do
lowdown \
--html-no-skiphtml \
--html-no-escapehtml < "$1/$f" |
render_html_file "$3" "$(head -n 3 ${1}/${f} | tail -n 1)" > "$2/${f%\.md}.html"
node ./enrich_codeblocks.js "$2/${f%\.md}.html"
done
}
render_md_files_Markdown_pl() {
while read -r f
do
Markdown.pl < "$1/$f" |
render_html_file "$3" \
> "$2/${f%\.md}.html"
done
}
render_html_file() {
# h/t Devin Teske
# TODO IMPROVE THIS IF POSSIBLE
awk -v title="${1}" -v brief="${2}" '
{ body = body "\n" $0 }
END {
body = substr(body, 2)
if (body ~ /<[Hh][Tt][Mm][Ll]/) {
print body
exit
}
if (match(body, /<[[:space:]]*[Hh]1(>|[[:space:]][^>]*>)/)) {
t = substr(body, RSTART + RLENGTH)
sub("<[[:space:]]*/[[:space:]]*[Hh]1.*", "", t)
gsub(/^[[:space:]]*|[[:space:]]$/, "", t)
if (t) title = t " &mdash; " title
}
n = split(ENVIRON["HEADER"], header, /\n/)
for (i = 1; i <= n; i++) {
if (match(tolower(header[i]), "<meta name=\"twitter:title\" content=\"\">")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<meta name=\"twitter:title\" content=\"" title "\">" tail
} else if (match(tolower(header[i]), "<meta name=\"og:title\" content=\"\">")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<meta name=\"og:title\" content=\"" title "\">" tail
} else if (match(tolower(header[i]), "<title></title>")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<title>" title "</title>" tail
} else if (match(tolower(header[i]), "<meta name=\"twitter:description\" content=\"\">")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<meta name=\"twitter:description\" content=\"" brief "\">" tail
} else if (match(tolower(header[i]), "<meta name=\"description\" content=\"\">")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<meta name=\"description\" content=\"" brief "\">" tail
} else if (match(tolower(header[i]), "<meta name=\"og:description\" content=\"\">")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<meta name=\"og:description\" content=\"" brief "\">" tail
} else print header[i]
}
print body
print ENVIRON["FOOTER"]
}'
}
list_pages() {
e="\\( -name '*.html' -o -name '*.md' \\)"
cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" |
sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
}
render_sitemap() {
urls="$1"
base_url="$2"
date="$3"
echo '<?xml version="1.0" encoding="UTF-8"?>'
echo '<urlset'
echo 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
echo 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'
echo 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"'
echo 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
echo "$urls" |
sed -E 's#^(.*)$#<url><loc>'"$base_url"'/\1</loc><lastmod>'\
"$date"'</lastmod><priority>1.0</priority></url>#'
echo '</urlset>'
}
main "$@"

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

8
templates/404.html Normal file
View File

@ -0,0 +1,8 @@
{% extends "terminimal/templates/404.html" %}
{% block content %}
<div class="post">
<h1 class="post-title">{% block heading %}Perdu ?{% endblock heading %}</h1>
<p>{% block message %}Cette page n'existe pas.{% endblock message %}</p>
</div>
{% endblock content %}

56
templates/index.html Normal file
View File

@ -0,0 +1,56 @@
{% extends "terminimal/templates/index.html" %}
{% block content %}
<div class="posts">
{%- if paginator %}
{%- set show_pages = paginator.pages -%}
{% else %}
{%- set show_pages = section.pages -%}
{% endif -%}
{%- for page in show_pages %}
<div class="post on-list">
{{ post_macros::header(page=page) }}
{{ post_macros::content(page=page, summary=true, show_only_description=page.extra.show_only_description | default(value=false)) }}
</div>
{% endfor -%}
<div class="pagination">
<div class="pagination__buttons">
{%- if paginator.previous %}
<span class="button previous">
<a href="{{ paginator.previous | safe }}">
<span class="button__icon"></span>&nbsp;
<span class="button__text">Posts plus récents</span>
</a>
</span>
{% endif -%}
{%- if paginator.next %}
<span class="button next">
<a href="{{ paginator.next | safe }}">
<span class="button__text">Posts plus anciens</span>&nbsp;
<span class="button__icon"></span>
</a>
</span>
{% endif -%}
</div>
</div>
</div>
{% endblock content %}
{% block footer %}
<footer class="footer">
<div class="footer__inner">
{%- if config.extra.copyright_html %}
<div class="copyright copyright--user">{{ config.extra.copyright_html | safe }}</div>
{% else %}
<div class="copyright">
<span>{{ config.extra.author }}</span>
<span>
<span>:: </span>
Made with ❤, <a href="https://gitlab.com/lovallat/blog" target="_blank">Open Source Software</a>, <a href="https://www.getzola.org" target="_blank">Zola</a> and <a href="https://github.com/pawroman/zola-theme-terminimal/" target="_blank">Terminimal</a>
</span>
</div>
{% endif -%}
</div>
</footer>
{% endblock footer %}

17
templates/tags/list.html Normal file
View File

@ -0,0 +1,17 @@
{% extends "terminimal/templates/tags/list.html" %}
{% block content %}
<div class="post">
<h1 class="post-title">Liste des tags</h1>
<ul>
{% for term in terms %}
<li class="tag-list">
<a href="{{ term.permalink | safe }}">
{{ term.name }} ({{ term.pages | length }} post{{ term.pages | length | pluralize }})
</a>
</li>
{% endfor %}
</ul>
</div>
{% endblock content %}

View File

@ -0,0 +1,17 @@
{% extends "terminimal/templates/tags/single.html" %}
{% block content %}
<div class="post">
<h1 class="post-title">
tag: #{{ term.name }}
({{ term.pages | length }} post{{ term.pages | length | pluralize }})
</h1>
<a href="{{ config.base_url | safe }}/tags">
Voir tous les tags
</a>
{{ post_macros::list_posts(pages=term.pages) }}
</div>
{% endblock content %}

1
themes/terminimal Submodule

@ -0,0 +1 @@
Subproject commit 0ced77898f37eb388181c4bdfa564febe437841e