Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 9 additions & 16 deletions .github/workflows/generate-pdf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ name: Pré-rendre les pages et générer les PDF

# Deux produits dérivés de js/data.js, régénérés et recommittés ici :
# 1. les pages pré-rendues, lues par les robots qui n'exécutent pas
# JavaScript (scripts/generate-static.js) : zones des trois pages HTML en
# français, et les versions anglaises en/index.html et en/results.html ;
# JavaScript (scripts/generate-static.js) : zones de index.html et
# results.html, leurs versions anglaises (en/), les pages des side projects
# (projets/, en/projects/) et la redirection project-detail.html ;
# 2. les deux PDF (scripts/generate-pdf.js).
# Se déclenche quand le contenu/style du CV change (pas sur le README ou
# d'autres fichiers sans rapport), ET une fois par mois indépendamment de
Expand Down Expand Up @@ -74,7 +75,7 @@ jobs:
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add index.html results.html project-detail.html en/index.html en/results.html assets/cv-antoine-berthaud-fr.pdf assets/cv-antoine-berthaud-en.pdf assets/cv-antoine-berthaud-fr-court.pdf assets/cv-antoine-berthaud-en-short.pdf
git add -A -- index.html results.html project-detail.html en projets assets/cv-antoine-berthaud-fr.pdf assets/cv-antoine-berthaud-en.pdf assets/cv-antoine-berthaud-fr-court.pdf assets/cv-antoine-berthaud-en-short.pdf
if git diff --cached --quiet; then
echo "Aucun changement dans les pages ni les PDF, rien à committer."
else
Expand All @@ -83,23 +84,15 @@ jobs:
python3 - "$(date -u +%Y-%m-%d)" $(git diff --cached --name-only) <<'EOF'
import re, sys, pathlib
today, changed = sys.argv[1], set(sys.argv[2:])
# Fichier du dépôt → URL(s) du sitemap qu'il porte.
urls = {
"index.html": ["https://cv.antoine.berthaud.me/"],
"en/index.html": ["https://cv.antoine.berthaud.me/en/"],
"results.html": ["https://cv.antoine.berthaud.me/results.html"],
"en/results.html": ["https://cv.antoine.berthaud.me/en/results.html"],
"project-detail.html": ["https://cv.antoine.berthaud.me/project-detail.html?slug=tour-de-growth"],
"assets/cv-antoine-berthaud-fr.pdf": ["https://cv.antoine.berthaud.me/assets/cv-antoine-berthaud-fr.pdf"],
"assets/cv-antoine-berthaud-en.pdf": ["https://cv.antoine.berthaud.me/assets/cv-antoine-berthaud-en.pdf"],
}
# Fichier du dépôt → URL du sitemap qu'il porte (index.html → dossier).
def url(f):
return "https://cv.antoine.berthaud.me/" + (f[: -len("index.html")] if f.endswith("index.html") else f)
p = pathlib.Path("sitemap.xml")
s = p.read_text()
total = 0
for f in sorted(changed):
for url in urls.get(f, []):
s, n = re.subn(r"(<loc>" + re.escape(url) + r"</loc>\s*<lastmod>)\d{4}-\d{2}-\d{2}", r"\g<1>" + today, s)
total += n
s, n = re.subn(r"(<loc>" + re.escape(url(f)) + r"</loc>\s*<lastmod>)\d{4}-\d{2}-\d{2}", r"\g<1>" + today, s)
total += n
p.write_text(s)
print(f"sitemap.xml : {total} lastmod mis à jour au {today} pour {', '.join(sorted(changed))}")
EOF
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ jobs:
# recommittera après le merge (durées mises à jour, par exemple).
- name: Écart entre les pages committées et le pré-rendu de la CI
run: |
if git diff --quiet -- index.html results.html project-detail.html en/ && [ -z "$(git ls-files --others --exclude-standard -- en/)" ]; then
if git diff --quiet -- index.html results.html project-detail.html en/ projets/ && [ -z "$(git ls-files --others --exclude-standard -- en/ projets/)" ]; then
echo "Pages pré-rendues à jour : aucun écart."
else
git --no-pager diff --stat -- index.html results.html project-detail.html en/
git ls-files --others --exclude-standard -- en/
git --no-pager diff -- index.html results.html project-detail.html en/ | head -120
git --no-pager diff --stat -- index.html results.html project-detail.html en/ projets/
git ls-files --others --exclude-standard -- en/ projets/
git --no-pager diff -- index.html results.html project-detail.html en/ projets/ | head -120
fi

- name: Générer les PDF FR et EN (sans les committer)
Expand Down
40 changes: 25 additions & 15 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ donnée : tout le contenu vit dans `js/data.js`, le rendu dans `js/app.js`.
statiques depuis la mission SEO du 24/09/2026) : les HTML portent la page
rendue entre des marqueurs `<!-- static:ID -->` … `<!-- /static:ID -->`
(une vingtaine de zones sur `index.html`, tout le `<main>` sur
`results.html` et `project-detail.html`) plus `<title>`, description et
`results.html` et les pages projet) plus `<title>`, description et
titres/descriptions `og:`/`twitter:` — c'est ce que lisent les robots sans
JavaScript (moteurs IA, aperçus, outils de tri de candidatures ; 258 →
~1 800 mots sur l'accueil). **Tout ce qui est entre deux marqueurs est
Expand All @@ -47,11 +47,14 @@ donnée : tout le contenu vit dans `js/data.js`, le rendu dans `js/app.js`.
`-placeholder`, `-aria`, `-alt` (menu, titres, `<noscript>`), repris
d'`i18n.js`. Les JS re-rendent par-dessus au chargement (idempotent) et
avertissent en console (`[cv] …`) si un texte statique diverge des sources :
page pas encore régénérée. `project-detail.html` (page gabarit `?slug=`)
est pré-rendue en français avec le seul side project, traduite en JS sur
`?lang=en` (avec `html.lang-pending`, body invisible jusqu'au rendu
anglais) ; le script refuse de tourner si `PROJECT_DETAILS` en compte plus
d'un — il faudra alors une page par projet. `scripts/check-seo.mjs`
page pas encore régénérée. **Side projects** : une page par langue et par
entrée de `PROJECT_DETAILS`, `projets/<slug>.html` et
`en/projects/<slug>.html`, entièrement générées depuis
`scripts/templates/project.html` (le gabarit, `<body data-slug>` posé par
le générateur) ; un projet retiré de `data.js` voit ses pages supprimées.
Les liens vers un projet s'écrivent `data-project-href="<slug>"` dans les
gabarits (le générateur pose l'adresse de la langue) ou
`i18n.projectUrl(slug)` en JS. `scripts/check-seo.mjs`
(`npm run check-seo`, serveur sur le port 8080) vérifie le tout, HTML brut
et rendu avec JS.
- **Tout fichier du dépôt qui n'est pas dans la liste `exclude` de `_config.yml`
Expand Down Expand Up @@ -80,11 +83,17 @@ donnée : tout le contenu vit dans `js/data.js`, le rendu dans `js/app.js`.
une seule annonce passe par `#fitAnnounce` (`role=status`) au départ et à
l'arrivée, `#fitError` est un `role=alert`, et le focus est posé sur
`#fitResult` (`tabindex=-1`) après le rendu.
- `js/project-detail.js` + `project-detail.html` : gabarit réutilisable
pour les pages de détail ("étude de cas") des side projects, piloté par
`?slug=`. Ajouter un nouveau side project avec sa page de détail ne
touche que `data.js` (nouvelle entrée `SIDE_PROJECTS` + `PROJECT_DETAILS`
liées par `detailSlug`) — jamais ces deux fichiers.
- `js/project-detail.js` + `scripts/templates/project.html` : rendu et
gabarit des pages de détail ("étude de cas") des side projects,
`/projets/<slug>.html` et `/en/projects/<slug>.html` (depuis la mission SEO
de sept. 2026 ; slug lu dans `<body data-slug>`, langue dans le chemin).
Ajouter un nouveau side project avec sa page de détail ne touche que
`data.js` (nouvelle entrée `SIDE_PROJECTS` + `PROJECT_DETAILS` liées par
`detailSlug`, avec `metaDescription`) et `sitemap.xml` — jamais ces deux
fichiers. `project-detail.html` n'est plus que l'ancienne adresse
(`?slug=`, `?lang=en`) : un script en tête du `<head>` redirige les slugs
connus (table générée), sinon « Projet introuvable » en noindex posé par
JS ; ni canonical, ni noindex, ni GoatCounter dans son HTML.
- `js/results.js` + `results.html` : page "Résultats", le détail (format
STAR : Contexte/Défi/Action/Résultat optionnel/Leçon) derrière les
chiffres cliquables du hero. Contrairement à `project-detail.js` (une
Expand Down Expand Up @@ -318,9 +327,9 @@ plein ont tous les deux été essayés et jugés trop nets avant ça.
calculer ; assets et scripts depuis la racine (`/assets/…`, `/js/…`). Ne
pas remettre de détection navigateur "pour les recruteurs anglophones" :
on leur partage `/en/`. Jamais de `<base href>` (casserait les ancres).
Exception provisoire : `project-detail.html?slug=` reste piloté par
`?lang=en` (`langSuffix()`/`syncUrl()` n'existent plus que pour elle)
jusqu'à son passage en URL statique. **Seule exception à « pas de
Les pages projet suivent la même règle (`/projets/…` ↔ `/en/projects/…`,
liens `../` relatifs) ; `langSuffix()`, `syncUrl()` et `lang-pending` ont
disparu avec leur passage en URL statique. **Seule exception à « pas de
détection navigateur » : `404.html`** (sept. 2026, décision d'Antoine).
GitHub Pages la sert pour toute URL inconnue ; un script inline garde le
bloc FR ou EN d'après l'URL cassée (chemin sous `/en/`, ancien `?lang=`),
Expand Down Expand Up @@ -425,7 +434,8 @@ plein ont tous les deux été essayés et jugés trop nets avant ça.
restée en français (vérifié en rendu Playwright avec `locale: "en-US"`).
Sur un site rendu en JS, la langue par défaut doit être décidée par l'URL,
jamais par le navigateur.
13. **Un `<link rel="canonical">` statique sur une page gabarit pilotée par
13. **(Historique : les pages projet sont statiques depuis la mission SEO de
sept. 2026, le principe reste.)** **Un `<link rel="canonical">` statique sur une page gabarit pilotée par
`?slug=`** (`project-detail.html`) pointait la page nue "Projet
introuvable" comme version de référence de l'étude de cas — l'`id`
`canonicalLink` existait mais n'était jamais mis à jour. Toute page dont
Expand Down
Loading
Loading