notabene
Publier un site public
Guide
Publier un site public
Publier un site public
L’app de revue est un outil local de dev — mais les docs qu’elle rend méritent
souvent un foyer public. build --public produit un artefact purement statique, en
lecture seule fait pour ça :
notabene build --public --site https://you.github.io --base /your-repo --out ./_site
Ce site même est construit ainsi — les docs de notabene, rendues et publiées par notabene.
Ce qui reste, ce qui part
- Tout l’interactif disparaît — structurellement. Pas d’UI de commentaires, pas
d’invite d’identité, pas de
/comments//review//journal, pas d’API, et rien du store.notabene. Les routes ne sont pas verrouillées ; elles ne sont pas construites. - Ce qui reste est l’expérience de lecture complète : nav, recherche, Mermaid +
lightbox d’images, mode sombre, i18n (pages par locale,
sélecteur,
hreflang), routes print/PDF,404. - Né lisible par les agents. Chaque page embarque un double Markdown à
<page>/index.md(annoncé via<link rel="alternate" type="text/markdown">), le site embarque/llms.txt(un index machine de chaque page, par locale) et/llms-full.txt(la doc entière en un seul document Markdown dans l’ordre de lecture), plusrobots.txt, un sitemap, des URL canoniques, des meta OpenGraph/Twitter et du JSON-LD. Essayez ici : /llms.txt.
Recherche plein texte (optionnelle)
Le site public hérite de la recherche intégrée telle quelle. Installez
Pagefind en dépendance de dev et build --public la promeut en
recherche plein texte statique :
npm i -D pagefind
Le build indexe l’artefact final : stemming par langue (un visiteur de /fr/ cherche
dans un index français, avec les formes fléchies du français), extraits surlignés sous
chaque résultat, et un poids réseau qui reste faible quand la doc grandit — le
navigateur ne télécharge que les morceaux d’index utiles à la requête. Zéro
configuration, même champ de recherche. Non installé → le site public garde la
recherche JSON intégrée. Et le contenu privé ne peut pas fuiter dans l’index :
l’indexation s’exécute sur l’artefact, où les
pages hors périmètre n’existent pas.
La même dépendance améliore aussi l’app de revue notabene dev : son index est
construit en direct depuis vos sources Markdown et rafraîchi quand elles changent —
aucun build en jeu. Deux différences propres au dev : le site de dev (donc son index)
inclut vos pages privées, et les résultats profonds par section (ancres de titres)
restent réservés au public.
Où aller ensuite
- Configurer
publish—site,base,exclude, avec exemples. - Garder du contenu privé — portée par espace / sous-arborescence / page.
- Domaine géré côté serveur — omettre
site, rester agnostique à l’origine. - Déployer via GitHub Pages — le workflow prêt à l’emploi.
La boucle de dev est intacte : notabene dev et le simple notabene build se
comportent exactement comme avant — la publication est opt-in, par build.
Configurer publish
Configurer publish
Tout vit sous une seule clé de config optionnelle — les flags CLI (--site/--base)
la surchargent, et --out copie l’artefact vers un chemin stable (il refuse d’écraser
quoi que ce soit qu’il n’a pas généré) :
// notabene.config.mjs
export default {
// …
publish: {
// ORIGINE déployée. Grave des URL absolues dans canonical, og:url, JSON-LD,
// hreflang, llms.txt, le sitemap et la ligne Sitemap de robots.txt.
// OPTIONNELLE — omettez-la pour garder le domaine hors du repo (voir
// « Domaine géré côté serveur »). Origine seule, sans chemin : un sous-chemin va dans `base`.
site: "https://you.github.io",
// Sous-chemin quand le site est servi sous un préfixe (site de projet
// GitHub Pages → "/<repo>"). Préfixe chaque lien et URL d'asset — contrairement
// au domaine, un sous-chemin affecte toujours le rendu, il ne peut pas être côté serveur.
base: "/your-repo",
// Sous-arborescences à garder hors des builds publics — globs comparés à
// `<space key>/<page id>` (indépendant de la locale : un motif masque chaque
// traduction d'une page). `*` = un segment de chemin, `**` = toute profondeur.
exclude: ["docs/internal/**", "docs/*/draft"],
},
};
Trois configurations typiques
publish: { site: "https://you.github.io", base: "/my-repo" } // GitHub Pages, site de projet
publish: { site: "https://docs.example.com" } // domaine personnalisé à la racine
publish: { exclude: ["docs/internal/**"] } // domaine gardé hors du repo
La troisième est le mode agnostique à l’origine — le même artefact derrière n’importe quel domaine.
Métadonnées par page
Deux clés de frontmatter alimentent les surfaces publiques (voir la référence complète du frontmatter) :
---
description: One-line summary — becomes the meta description / OpenGraph.
publish: false # cette page ne part jamais dans un build public
---
Exclure du contenu du build a sa propre page : garder du contenu privé.
Garder du contenu privé
Garder du contenu privé
Trois niveaux, du plus grossier au plus fin — chacun vit là où vit ce qu’il délimite.
Un espace entier — marquez l’entrée roots[] :
roots: [
{ key: "docs", label: "Docs", path: "docs" },
{ key: "notes", label: "Notes", path: "notes", publish: false }, // l'espace entier reste privé
],
Une sous-arborescence — des globs publish.exclude sur <space key>/<page id>
(c’est le chemin d’URL de la page sans préfixe de locale, donc un seul motif masque
toutes les traductions) :
publish: { exclude: ["docs/internal/**", "docs/*/draft"] },
Une seule page — son propre frontmatter :
---
publish: false # cette page n'apparaît jamais dans un build public
---
Un lien de navigation — pas du contenu, mais la même idée : une
entrée nav marquée publish: false reste en
dev et n’atteint jamais l’artefact (un dashboard, un wiki interne) :
nav: { header: [{ label: "Dashboard ops", href: "https://ops.internal", publish: false }] },
La garantie
Le contenu privé n’est pas caché, il n’est pas construit — pas de route (l’URL
renvoie un 404), pas d’entrée dans la sidebar, pas de résultat de recherche, pas de
ligne llms.txt, pas de double .md, pas d’entrée de sitemap, pas d’inclusion
print/PDF, et le nom et le chemin de l’espace n’atteignent jamais le HTML public.
notabene dev et les builds normaux montrent toujours tout — vous
passez en revue vos docs privées exactement comme le reste. La
notion de publish n’existe que pour les builds publics.
Une réserve : les liens vers du contenu privé
Si une page publique pointe vers une page privée, ce lien renvoie un 404 dans
l’artefact public — le build ne le réécrit pas et n’émet aucun avertissement.
notabene lint attrape exactement cela : lancez-le après build --public et chaque
lien d’une page publique vers du contenu exclu par le scoping est signalé (la vérité des
routes publique ne contient tout simplement pas ces pages — voir la
référence CLI).
Domaine géré côté serveur
Domaine géré côté serveur ? Omettez site
Quand le domaine public est l’affaire du serveur — un vhost ou un reverse proxy en
frontal, plusieurs miroirs, ou un domaine pas encore choisi — ne définissez simplement
pas site (et ne passez pas de --site). L’artefact devient agnostique de
l’origine : pas une seule URL absolue n’y est embarquée, la même sortie fonctionne
donc derrière n’importe quel domaine, et changer de domaine n’exige jamais de rebuild.
Ce qui change
| Surface | Avec site | Sans |
|---|---|---|
llms.txt / llms-full.txt / doubles .md | URL absolues | chemins relatifs à la racine (les agents les résolvent contre l’origine depuis laquelle ils les ont récupérés) |
alternates hreflang | absolues | par chemins |
canonical, og:url, JSON-LD | émis | non émis — ils n’ont de sens qu’avec une origine |
sitemap + ligne Sitemap: de robots.txt | émis | non émis — les spécifications exigent des URL absolues |
Ce que cela coûte, concrètement
La visibilité dans les moteurs de recherche — rien d’autre. Sans sitemap, URL
canoniques ni hreflang valide, les crawlers ne découvrent les pages qu’en suivant les
liens, rien ne consolide les doublons si la doc répond sur plusieurs domaines, et les
pages multilingues n’envoient aucun signal de langue aux moteurs de recherche. Les
aperçus de liens sociaux gardent leur titre/description mais perdent la carte d’URL.
Les lecteurs humains et les agents IA ne perdent rien — chaque page, double et
fichier llms fonctionne à l’identique.
Règle simple : hébergement interne, miroir, ou domaine pas encore arrêté → omettez
site ; un site public dont le référencement compte → définissez site.
base reste indépendant
Définissez base dès que le site vit sous un sous-chemin, avec ou
sans domaine — un sous-chemin affecte toujours les liens rendus, il ne peut donc pas
être laissé au serveur.
Déployer via GitHub Pages
Déployer via GitHub Pages
Déployez sur n’importe quel hébergement statique. Pour GitHub Pages : Settings → Pages → Source = GitHub Actions, puis :
# .github/workflows/docs.yml
name: docs
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm ci
- run: npx notabene build --public
--site https://${{ github.repository_owner }}.github.io
--base /${{ github.event.repository.name }}
--out ./_site
- uses: actions/upload-pages-artifact@v3
with: { path: ./_site }
- id: deployment
uses: actions/deploy-pages@v4
Notes :
- Avec
publish: { site, base }défini dans votre config, les flags--site/--basepeuvent être omis entièrement. - Hébergement sur un domaine personnalisé ou un site racine utilisateur/organisation ?
Retirez
--baseet pointez--sitevers votre domaine. - L’artefact embarque un
.nojekyll, donc les assets_astro/survivent même à l’hébergement classique par branche gh-pages. npm i -D pagefinddans votre repo et ce même workflow embarque aussi la recherche plein texte —npm cil’installe, le build la détecte. Rien à changer ici.- Ce site de documentation est déployé par exactement ce workflow — voyez-le dans le repo.