Retex — Afficher la date de dernier push sur le site
Comment afficher « Dernière mise à jour le 7 mai 2026 » dans le footer de la page À propos. Première version en JS côté client, deuxième version injectée au build par la CI. Le récit explique pourquoi la deuxième est meilleure que la première — et ce qu'on perd au passage.
La fiche CI/CD et le retex CI/CD du site couvrent comment on a un workflow GitHub Actions qui déploie ce site. Cette fiche-ci raconte une extension de ce workflow : faire faire au CI autre chose que vérifier les liens, en l'occurrence modifier le HTML avant la publication.
1. Le besoin
Sur la page À propos, je voulais afficher « Dernière mise à jour le … » avec la date du dernier push, pour qu'un visiteur sache si le site est actif ou abandonné depuis 6 mois.
Deux contraintes :
- Le site est statique (HTML/CSS/JS pur). Pas de backend qui pourrait calculer la date à la volée.
- La date doit être automatique. Hors de question d'éditer la valeur à la main à chaque commit.
2. Première version — fetch côté client
Réflexe naturel : appeler l'API GitHub depuis le navigateur du visiteur.
Dernière mise à jour le <span id="last-update">—</span>
<script>
fetch("https://api.github.com/repos/os974/devianotes/commits?per_page=1")
.then(r => r.json())
.then(data => {
const d = new Date(data[0].commit.committer.date);
document.getElementById("last-update").textContent =
d.toLocaleDateString("fr-FR", { day: "2-digit", month: "long", year: "numeric" });
});
</script>
Ça marche du premier coup. Mais en y regardant de plus près, trois petites gênes accumulées :
- Un appel réseau à chaque visite vers
api.github.com. Anonyme, donc soumis à un quota (60 req/h par IP). Pour un site à 10 visites/jour aucun souci, mais c'est de la dette latente. - CSP à élargir : il faut ajouter
connect-src 'self' https://api.github.comauContent-Security-Policy. On ouvre une porte juste pour afficher une date. - Désynchronisation possible : si l'API renvoie un commit plus récent que le HTML déployé (ex. tu pushes deux fois et regardes pendant le second build), la page affiche une date qui ne correspond pas à ce qui est servi. Subtil, mais incohérent.
3. Deuxième version — injection au build
Le déclic : la date est connue au moment du déploiement. Pas besoin de la calculer chez le visiteur, autant la figer dans le HTML avant publication.
La page contient un placeholder, le workflow le remplace au moment du déploiement.
Côté HTML — un placeholder
Dernière mise à jour le <span id="last-update">__LAST_UPDATE__</span>
__LAST_UPDATE__ est une chaîne improbable choisie pour ne
risquer aucun faux positif lors du remplacement. Le double underscore
signale visuellement que c'est un marker, pas du contenu.
Côté CI — un step qui remplace
- name: Injecter la date de dernier push dans about.html
run: |
months=(janvier février mars avril mai juin juillet août septembre octobre novembre décembre)
day=$(git log -1 --format=%cd --date=format:%-d)
month_idx=$(git log -1 --format=%cd --date=format:%-m)
year=$(git log -1 --format=%cd --date=format:%Y)
date_fr="${day} ${months[$((month_idx-1))]} ${year}"
sed -i "s|__LAST_UPDATE__|${date_fr}|g" about.html
# garde-fou : si le placeholder n'a pas été remplacé, on échoue
if grep -q "__LAST_UPDATE__" about.html; then
echo "::error::Placeholder __LAST_UPDATE__ encore présent"
exit 1
fi
Ce step est inséré entre actions/checkout@v4 et
actions/upload-pages-artifact@v3. Il modifie la copie de
about.html dans la VM du runner — la modif n'est jamais
commitée, elle vit le temps du job, exactement comme un build classique
produit un dist/ jetable.
4. Les leçons
La date d'un déploiement ne change pas après coup. La calculer chez le visiteur, c'est faire payer à 100 % des visites un calcul qui aurait pu être fait une seule fois par le CI.
Règle générale : si une donnée est connue au moment où tu construis le site, fige-la dans le HTML servi. Tu n'utilises du JS côté client que pour ce qui dépend du visiteur (interactions, état local, données vraiment temps réel).
En passant de v1 à v2, j'ai pu retirer
connect-src https://api.github.com du
Content-Security-Policy. Le navigateur ne fait plus
aucune requête sortante depuis cette page. Plus la CSP est étroite,
plus une éventuelle XSS injectée a de mal à exfiltrer des données.
Penser réseau chaque fois qu'on écrit du JS : « cette feature a-t-elle vraiment besoin de causer à un serveur ? ». Souvent non, et on s'épargne une autorisation CSP.
Pour formater « 7 mai 2026 », j'ai d'abord pensé à
LC_TIME=fr_FR.UTF-8 date "+%-d %B %Y". Mais
ubuntu-latest n'a que en_US et
C.UTF-8 installées par défaut. Soit on lance
locale-gen fr_FR.UTF-8 (~5 secondes ajoutées au job),
soit on tape la liste des mois dans un tableau bash.
Pour 12 strings figées qui ne changeront jamais, le tableau gagne : zéro dépendance, zéro temps installation, lisible en deux lignes. L'approche « install locale » a sa place dès qu'on en a besoin ailleurs — pas pour un seul format de date.
sed -i "s|...|...|g" ne renvoie pas d'erreur
si le motif est absent : il ne remplace simplement rien. Si demain
je renomme __LAST_UPDATE__ dans le HTML sans toucher au
workflow, le CI passera vert et la page sera publiée avec le motif
d'origine encore visible. Bug silencieux.
D'où le grep -q "__LAST_UPDATE__" && exit 1
après le sed : si le placeholder est encore là après remplacement,
c'est que le contrat HTML/CI est cassé, on échoue fort avec un
message clair.
Pattern réutilisable : après chaque transformation automatique, vérifier que le résultat correspond à ce qu'on attendait. C'est l'équivalent CI d'un test unitaire — sauf qu'on teste la sortie de l'outil, pas du code applicatif.
Si tu ouvres about.html en local par double-clic (sans
passer par GitHub Pages), tu vois littéralement
« Dernière mise à jour le __LAST_UPDATE__ ». Pas joli.
On pourrait ajouter un fallback JS qui détecte le placeholder et affiche « (version locale) » à la place. Mais ça rajoute du code pour cacher un problème qui ne se produit que dans un cas marginal (dev local, page À propos). À ce stade, accepter le placeholder visible est le bon choix : le coût (un mot moche pendant qu'on développe) est inférieur au bénéfice du code plus simple.
Règle générale : ne complexifie pas le code pour des cas que personne ne voit en condition réelle. Documente le trade-off (commentaire HTML à côté du placeholder) et passe à la suite.
5. Quand utiliser ce pattern
L'injection au build est utile dès que tu as besoin d'afficher dans une page statique une donnée qui :
- est connue au moment du déploiement (date, numéro de version, hash du commit, environnement, nom du build) ;
- ne change pas entre deux déploiements ;
- n'a pas besoin d'être différente d'un visiteur à l'autre.
Cas typiques : badge de version (v1.4.2 dans un footer),
SHA du commit pour debug (build: a1b2c3d), date de
génération, banner d'environnement (« staging »). Dès que la donnée
dépend du visiteur ou du temps présent (heure courante, données live),
on revient à du JS côté client ou un appel API.
Liens vers les fiches concernées
- CI/CD — la théorie générale.
- GitHub — Actions et Pages.
- Retex — CI/CD du site — le workflow de base sur lequel ce step s'est greffé.