diff --git a/.github/actions/build-portal/action.yml b/.github/actions/build-portal/action.yml new file mode 100644 index 0000000..4c7db93 --- /dev/null +++ b/.github/actions/build-portal/action.yml @@ -0,0 +1,55 @@ +name: Construction et contrôle du portail +description: >- + Étapes communes au déploiement et à la vérification des pull requests : construction stricte du + site, contrôle d'accessibilité des pages, export des PDF et validation PDF/UA-1. Le site construit + reste dans ./site pour les étapes suivantes du workflow appelant. + +runs: + using: composite + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + + - name: Install dependencies + shell: bash + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt -r requirements-pdf.txt + sudo apt-get update + sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 poppler-utils + + - name: Build documentation + shell: bash + run: mkdocs build --strict + + # Accessibilité des pages HTML (WCAG 2 AA, moteur axe) : Chrome du runner, sans téléchargement + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Check accessibility (pa11y, axe) + shell: bash + env: + PUPPETEER_SKIP_DOWNLOAD: "true" + CHROME: /usr/bin/google-chrome + run: | + npm install --no-save --no-audit --no-fund pa11y@9 + node scripts/check_a11y.js site + + - name: Export PDF + shell: bash + run: python scripts/export_pdf.py + + - name: Cache veraPDF + uses: actions/cache@v4 + with: + path: ~/.cache/verapdf + key: verapdf-1.30.2 + + - name: Validate PDF/UA-1 + shell: bash + run: bash scripts/check_pdf_ua.sh diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index a6c944e..c7526e5 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -1,6 +1,6 @@ name: Vérification des pull requests (build, accessibilité et PDF) -# Mêmes étapes de construction que le déploiement, sans déploiement ni secret : +# Mêmes étapes de construction que le déploiement (.github/actions/build-portal), sans déploiement ni secret : # le site et les PDF du portail (livre blanc, statuts, règlement intérieur, charte) sont validés # avant fusion, et les PDF joints en artefact pour relecture. @@ -24,47 +24,8 @@ jobs: with: persist-credentials: false - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - cache: pip - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt -r requirements-pdf.txt - sudo apt-get update - sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 poppler-utils - - - name: Build documentation - run: mkdocs build --strict - - # Accessibilité des pages HTML (WCAG 2 AA, moteur axe) : Chrome du runner, sans téléchargement - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - - - name: Check accessibility (pa11y, axe) - env: - PUPPETEER_SKIP_DOWNLOAD: "true" - CHROME: /usr/bin/google-chrome - run: | - npm install --no-save --no-audit --no-fund pa11y@9 - node scripts/check_a11y.js site - - - name: Export PDF - run: python scripts/export_pdf.py - - - name: Cache veraPDF - uses: actions/cache@v4 - with: - path: ~/.cache/verapdf - key: verapdf-1.30.2 - - - name: Validate PDF/UA-1 - run: bash scripts/check_pdf_ua.sh + - name: Build and check the portal + uses: ./.github/actions/build-portal # Polices embarquées : Inter pour le texte ; toute autre police signale un glyphe absent d'Inter - name: List embedded fonts diff --git a/.github/workflows/deploy-o2switch.yml b/.github/workflows/deploy-o2switch.yml index 922a784..caa8aad 100644 --- a/.github/workflows/deploy-o2switch.yml +++ b/.github/workflows/deploy-o2switch.yml @@ -21,33 +21,12 @@ jobs: with: persist-credentials: false - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - cache: pip - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt -r requirements-pdf.txt - sudo apt-get update - sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 lftp - - - name: Build documentation - run: mkdocs build --strict - - - name: Export whitepaper PDF - run: python scripts/export_pdf.py - - - name: Cache veraPDF - uses: actions/cache@v4 - with: - path: ~/.cache/verapdf - key: verapdf-1.30.2 + # Construction stricte, accessibilité, PDF et PDF/UA-1 : mêmes contrôles que les pull requests + - name: Build and check the portal + uses: ./.github/actions/build-portal - - name: Validate PDF/UA-1 - run: bash scripts/check_pdf_ua.sh + - name: Install lftp + run: sudo apt-get install -y lftp - name: Add Apache configuration run: cp deploy/o2switch/htaccess site/.htaccess diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b0b14ce --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: Release (PDF et brouillon Zenodo) + +# À la publication d'une release GitHub (étiquette vX.Y, numéro de version du livre blanc) : +# - reconstruit et contrôle le portail à cette étiquette, puis joint à la release les cinq PDF +# (livre blanc FR et EN, projet de statuts, règlement intérieur, charte d'éthique) ; +# - si le secret ZENODO_TOKEN existe, dépose le livre blanc sur Zenodo en brouillon +# (scripts/zenodo_draft.py) : le DOI n'est attribué qu'à la publication manuelle depuis Zenodo. +# Ne pas activer en plus l'intégration GitHub de Zenodo, qui créerait un second dépôt par release. + +on: + release: + types: [published] + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + persist-credentials: false + + - name: Build and check the portal + uses: ./.github/actions/build-portal + + - name: Attach PDF to the release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.event.release.tag_name }} + run: gh release upload "$TAG" $(find site -name 'otspi-*.pdf' | sort) --clobber --repo "$GITHUB_REPOSITORY" + + - name: Zenodo draft (white paper) + env: + ZENODO_TOKEN: ${{ secrets.ZENODO_TOKEN }} + ZENODO_URL: ${{ vars.ZENODO_URL }} + ZENODO_DEPOSITION_ID: ${{ vars.ZENODO_DEPOSITION_ID }} + TAG: ${{ github.event.release.tag_name }} + run: | + if [ -z "$ZENODO_TOKEN" ]; then + echo "Secret ZENODO_TOKEN absent : pas de dépôt Zenodo." | tee -a "$GITHUB_STEP_SUMMARY" + exit 0 + fi + python scripts/zenodo_draft.py --version "${TAG#v}" \ + site/livre-blanc/otspi-livre-blanc.pdf site/white-paper/otspi-white-paper.pdf diff --git a/README.md b/README.md index c26b7b1..032bb85 100644 --- a/README.md +++ b/README.md @@ -42,38 +42,76 @@ La gouvernance d'OTSPI applique une séparation stricte des devoirs conformémen ## 📁 Panoplie Documentaire Complète ``` -├── docs/ -│ ├── index.md # Page d'accueil du portail web about.otspi.org -│ ├── CNAME # Configuration du domaine personnalisé (about.otspi.org) +├── docs/ # Contenu du portail about.otspi.org +│ ├── index.md # Page d'accueil (bandeau dans overrides/home.html) +│ ├── livre-blanc/ # Livre blanc (FR, fait foi) et historique des versions +│ ├── white-paper/ # Traduction anglaise du livre blanc │ ├── statuts/ -│ │ └── statuts-association.md # Statuts constitutifs adoptés (OTSPI, Lyon, 20 septembre 2026) +│ │ └── statuts-association.md # Projet de statuts, soumis à l'AG constitutive (non encore adopté) │ ├── reglement-interieur/ -│ │ └── reglement-interieur.md # RI complet : cursus Officiers, MFA FIPS/ANSSI, Key Custodians, dépenses -│ ├── administratif/ -│ │ ├── pv-ag-constitutive-modele.md # PV d'AG Constitutive (élections, mandats fiscaux L.80 CB, RC Pro) -│ │ ├── rescrit-fiscal-mecenat.md # Demande formelle de rescrit fiscal DGFIP (Méthode 4P) -│ │ └── declaration-prefecture.md # Guide des formalités préfecture (RNA, JOAFE, SIRET) -│ ├── cadrage/ -│ │ ├── index.md # Présentation du socle d'audit initial TSP / PKI -│ │ ├── cp-cps-cadre.md # Cadre général CP/CPS conforme RFC 3647 & ETSI EN 319 411 -│ │ ├── pssi.md # PSSI (ISO 27001 & ETSI EN 319 401, RSSI, incidents) -│ │ └── termination-plan.md # Plan de fin d'activité (archivage 10-30 ans, séquestre financier) +│ │ └── reglement-interieur.md # RI : cursus Officiers, MFA FIPS/ANSSI, Key Custodians, dépenses │ ├── gouvernance/ -│ ├── charte-ethique.md # Charte d'éthique, de déontologie et de gestion désintéressée -│ │ └── comite-technique.md # Articulation CPC (PMA), TSC (ingénierie logicielle) & RFCs +│ │ ├── charte-ethique.md # Charte d'éthique, de déontologie et de transparence publique +│ │ └── comite-technique.md # Articulation CPC (PMA), TSC (ingénierie logicielle) et RFC +│ ├── administratif/ # PV d'AG constitutive (modèle), rescrit fiscal, guide préfecture… +│ ├── cadrage/ # Socle d'audit TSP / PKI : CP/CPS, PSSI, fin d'activité, OID, devis │ ├── adhesion/ -│ │ └── bulletin-adhesion.md # Formulaires d'adhésion (sympathisants, titulaires, bienfaiteurs) -│ └── reunions/ -│ └── index.md # Registre public des réunions et procès-verbaux (caviardage RGPD) -├── mkdocs.yml # Configuration Material for MkDocs (about.otspi.org) -├── .github/workflows/ -│ └── deploy-o2switch.yml # Déploiement automatique chez o2switch (FTPS) -├── LICENSE # Licence Creative Commons Attribution 4.0 International -└── README.md # Documentation générale du dépôt +│ │ └── bulletin-adhesion.md # Formulaires d'adhésion (sympathisants, titulaires, bienfaiteurs) +│ ├── reunions/ # Registre des réunions, transparence des hébergements (FR / EN) +│ ├── assets/ # Logos, police Inter (OFL), image de partage +│ ├── stylesheets/ # Charte du portail (extra.css), impression et PDF (print.css) +│ └── javascripts/ # Accessibilité, mesure d'audience sans cookie +├── overrides/ # Gabarits Material : accueil, 404, métadonnées de partage +├── hooks/page_lang.py # Langue de la balise selon la page +├── scripts/ # Outils : voir ci-dessous +├── data/qtsp-count.json # Décompte des QTSP cité par le livre blanc (annexe C) +├── deploy/o2switch/ # Configuration Apache (.htaccess) et notes de déploiement +├── .github/workflows/ # Déploiement, vérification des PR, contrôles périodiques +├── mkdocs.yml # Configuration Material for MkDocs +└── LICENSE # Creative Commons Attribution 4.0 International ``` --- +## 🛠️ Outils et contribution + +Le portail est construit avec [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/). Toute pull request vers `main` est vérifiée par `.github/workflows/check-pr.yml` (construction stricte, accessibilité, PDF) ; la fusion déclenche le déploiement (`deploy-o2switch.yml`), qui repasse les mêmes contrôles. Les deux workflows partagent l'action `.github/actions/build-portal`. + +**Construire et prévisualiser le site** + +```sh +pip install -r requirements.txt +mkdocs serve # http://127.0.0.1:8000, rechargé à chaque modification +mkdocs build --strict # échoue sur tout lien ou ancre interne cassé +``` + +**Scripts** (`scripts/`) + +| Script | Rôle | Prérequis | +|---|---|---| +| `export_pdf.py` | PDF balisés du livre blanc (FR, EN), du projet de statuts, du règlement intérieur et de la charte, dans `site/` | `pip install -r requirements-pdf.txt` (WeasyPrint, Pango) | +| `check_pdf_ua.sh` | Validation PDF/UA-1 de ces PDF avec veraPDF (téléchargé et vérifié au premier lancement) | Java | +| `check_a11y.js` | Accessibilité WCAG 2 AA (pa11y, moteur axe) de toutes les pages du site construit | Node.js, `npm install --no-save pa11y@9`, Chrome | +| `build_diagrams.py` | Régénère les schémas SVG du livre blanc et l'organigramme de l'accueil | — | +| `build_og_image.py` | Régénère l'image de partage par défaut (`docs/assets/og/og-portail.png`) | Chrome | +| `zenodo_draft.py` | Brouillon Zenodo du livre blanc à la publication d'une release (`release.yml`) ; le DOI n'est attribué qu'à la publication manuelle sur Zenodo | secret `ZENODO_TOKEN` | +| `count_qtsp.py` | Décompte des QTSP des listes de confiance de l'EEE (`data/qtsp-count.json`) | — | +| `check_links.py` | Liens externes cassés (contrôle mensuel) | — | +| `check_sites.sh` | Codes HTTP, redirections et certificats des sites OTSPI (contrôle quotidien) | curl, openssl | + +Chaîne complète, comme en CI : + +```sh +mkdocs build --strict +python scripts/export_pdf.py +bash scripts/check_pdf_ua.sh +npm install --no-save pa11y@9 && node scripts/check_a11y.js site +``` + +Les schémas et l'image de partage sont générés puis versionnés : après modification de leur source, relancer le script correspondant et commiter le résultat. + +--- + ## 📬 Contact - **Contact unique (Adhésions, Questions générales, Sécurité)** : `contact@otspi.org` diff --git a/deploy/o2switch/README.md b/deploy/o2switch/README.md index a327d13..1fce292 100644 --- a/deploy/o2switch/README.md +++ b/deploy/o2switch/README.md @@ -5,7 +5,7 @@ Ce dossier décrit l'hébergement du portail `about.otspi.org` chez o2switch (so ## Ce qui est en place - `htaccess` : redirection HTTPS, domaine canonique, page 404, en-têtes de sécurité, compression et durées de cache. Il est copié à la racine du site au déploiement. -- `.github/workflows/deploy-o2switch.yml` : construit le site, exporte et valide les PDF (PDF/UA-1), ajoute le `.htaccess`, puis envoie tout en FTPS (TLS obligatoire, certificat vérifié). +- `.github/workflows/deploy-o2switch.yml` : construit et contrôle le site avec l'action commune `.github/actions/build-portal` (construction stricte, accessibilité, export et validation PDF/UA-1 des PDF, mêmes contrôles que les pull requests), ajoute le `.htaccess`, puis envoie tout en FTPS (TLS obligatoire, certificat vérifié). - Un compte FTP dédié, cantonné au seul répertoire du site. Il est renseigné dans les secrets `O2_FTP_USERNAME` et `O2_FTP_PASSWORD` du dépôt ; l'hôte est dans la variable `O2_FTP_HOST`. ## Bascule (faite le 26 septembre 2026) diff --git a/docs/participer/index.md b/docs/participer/index.md new file mode 100644 index 0000000..b87ae70 --- /dev/null +++ b/docs/participer/index.md @@ -0,0 +1,67 @@ +--- +title: "Participer à OTSPI" +description: "Comment contribuer à OTSPI pendant sa constitution : consultation sur le livre blanc, manifeste, partenariats, code, documents de gouvernance et signalement de vulnérabilités." +--- + +# Participer + +OTSPI est **en cours de constitution** : l'association n'est pas encore déclarée et ne recueille pas d'adhésions. Il est pourtant déjà possible de contribuer au projet, publiquement et sans inscription préalable. + +
+ +- :material-comment-text-multiple-outline:{ .lg .middle } __Commenter le livre blanc__ + + --- + + Le livre blanc est en consultation publique : remarques, objections et propositions sont reçues sur GitHub Discussions, où elles restent lisibles par tous. + + [:octicons-arrow-right-24: Donner votre avis](https://github.com/otspi/organisation/discussions/new?category=ideas) + [:octicons-arrow-right-24: Lire le livre blanc](../livre-blanc/index.md) + +- :material-draw-pen:{ .lg .middle } __Signer le manifeste__ + + --- + + Le Manifeste pour une identité numérique libre et ouverte rassemble celles et ceux qui soutiennent des services de confiance conçus comme un commun numérique. + + [:octicons-arrow-right-24: Lire et signer le manifeste](https://www.otspi.org/manifeste.html) + +- :material-handshake-outline:{ .lg .middle } __Devenir partenaire__ + + --- + + Hébergeurs, auditeurs, laboratoires, collectivités, financeurs : les besoins et les postes de dépense sont décrits dans le livre blanc. + + [:octicons-arrow-right-24: Proposer un partenariat](mailto:contact@otspi.org?subject=Partenariat%20OTSPI) + [:octicons-arrow-right-24: Postes de dépense (livre blanc, § 5.5)](../livre-blanc/index.md#55-structure-de-couts-et-garanties) + +- :material-code-braces:{ .lg .middle } __Contribuer au code__ + + --- + + Le moteur de services de confiance et le démonstrateur sont publiés sous licence libre (EUPL 1.2) ; un pilote d'horodatage RFC 3161 fonctionne déjà, en environnement d'essai non qualifié. + + [:octicons-arrow-right-24: Moteur (Rust)](https://github.com/otspi/open-eidas) + [:octicons-arrow-right-24: Démonstrateur](https://github.com/otspi/demo) + [:octicons-arrow-right-24: Essayer le pilote](https://demo.open-eidas.eu/) + +- :material-file-document-edit-outline:{ .lg .middle } __Améliorer les documents__ + + --- + + Statuts, règlement intérieur, charte et socle de conformité sont versionnés sur GitHub : chaque page du portail propose un lien d'édition, et toute modification passe par une proposition publique. + + [:octicons-arrow-right-24: Dépôt des documents](https://github.com/otspi/organisation) + [:octicons-arrow-right-24: Historique du projet de statuts](../statuts/historique.md) + +- :material-shield-alert-outline:{ .lg .middle } __Signaler une vulnérabilité__ + + --- + + Les signalements de sécurité suivent une divulgation coordonnée : écrire à `contact@otspi.org` avec `[Sécurité]` en objet, ou passer par les avis de sécurité privés de GitHub. + + [:octicons-arrow-right-24: Avis de sécurité privés](https://github.com/otspi/organisation/security/advisories) + +
+ +L'actualité du projet est publiée sur le [site de l'association](https://www.otspi.org/actualites.html), avec un flux Atom. diff --git a/docs/statuts/historique.md b/docs/statuts/historique.md new file mode 100644 index 0000000..2d11f33 --- /dev/null +++ b/docs/statuts/historique.md @@ -0,0 +1,15 @@ +# Historique du projet de statuts + +**Ce qui a changé dans le projet, et quand** + +Les statuts d'OTSPI sont un **projet**, soumis au vote de l'assemblée générale constitutive, qui n'a pas encore eu lieu. Cette page retrace les modifications de fond apportées au projet depuis sa première rédaction ; le [dépôt du portail](https://github.com/otspi/organisation/commits/main/docs/statuts/statuts-association.md) conserve l'historique complet, ligne à ligne. + +| Date | Modification | +|---|---| +| 6 septembre 2026 | Premier projet, sous le nom « Open eIDAS » : association d'intérêt général à but non lucratif, consacrée à des infrastructures de confiance numérique ouvertes. Le même jour : conformité aux critères de l'intérêt général (articles 200 et 238 bis du CGI) et aux principes des associations reconnues d'utilité publique ; bénévolat strict des dirigeants, avec la possibilité d'employer un personnel technique rémunéré sans rôle dans la gouvernance ; contribution au prix coûtant pour les usages massifs, recettes intégralement réinvesties. | +| 20 septembre 2026 | Refonte sous le nom « Open Trusted Service Provider Initiative » (OTSPI), avec le règlement intérieur et les documents de gouvernance. Création du statut de membre bienfaiteur, sans droit de vote. | +| 21 septembre 2026 | Déchéance d'un membre titulaire portée de deux à trois absences consécutives à l'assemblée générale ordinaire (article 5 ter). | +| 25 septembre 2026 | Texte présenté comme un projet non encore adopté ; la date de signature est laissée à compléter le jour de l'assemblée générale constitutive. Ajout de la vacance initiale du CPC (article 8 bis, alinéa 5 bis) : tant que le comité n'est pas constitué, ses compétences exclusives ne peuvent être exercées par aucun autre organe. | +| 27 septembre 2026 | Mise en forme, sans modification du texte : listes rétablies. Version PDF balisée, téléchargeable depuis la page du projet de statuts. | + +Le [règlement intérieur](../reglement-interieur/reglement-interieur.md) et la [charte d'éthique](../gouvernance/charte-ethique.md) suivent le projet de statuts ; leurs modifications sont consultables dans le même dépôt. diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index 3ecfb54..5337f1e 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -102,6 +102,109 @@ --md-footer-bg-color--dark: #070b16; } +/* -------------------------------------------------------------------------- + Thème clair : en-tête blanc, comme www.otspi.org (le thème sombre garde le bleu nuit) + -------------------------------------------------------------------------- */ +.md-logo img.otspi-logo--dark, +[data-md-color-scheme="slate"] .md-logo img.otspi-logo--light { + display: none; +} + +[data-md-color-scheme="slate"] .md-logo img.otspi-logo--dark { + display: block; +} + +[data-md-color-scheme="default"] .md-header, +[data-md-color-scheme="default"] .md-tabs { + background-color: #ffffff; + color: var(--otspi-navy); +} + +[data-md-color-scheme="default"] .md-header { + border-bottom: 0; +} + +[data-md-color-scheme="default"] .md-tabs { + border-bottom: 1px solid #e2e8f0; +} + +[data-md-color-scheme="default"] .md-tabs__item--active .md-tabs__link { + color: var(--otspi-navy); + box-shadow: inset 0 -2px 0 var(--otspi-primary); +} + +[data-md-color-scheme="default"] .md-header--shadow { + box-shadow: 0 2px 10px rgba(15, 23, 42, 0.08); +} + +/* Champ de recherche lisible sur fond blanc */ +@media screen and (min-width: 60em) { + [data-md-color-scheme="default"] .md-search__form { + background-color: #f1f5f9; + } + + [data-md-color-scheme="default"] .md-search__input, + [data-md-color-scheme="default"] .md-search__icon { + color: var(--otspi-navy); + } + + [data-md-color-scheme="default"] .md-search__input::placeholder { + color: #475569; + } +} + +/* Titre du menu latéral (mobile), assorti à l'en-tête */ +[data-md-color-scheme="default"] .md-nav--primary .md-nav__title[for="__drawer"] { + background-color: #ffffff; + color: var(--otspi-navy); + border-bottom: 1px solid #e2e8f0; +} + +/* Appel à l'action commun avec la vitrine (overrides/partials/source.html) */ +.otspi-cta { + display: inline-block; + flex: none; + margin-right: 0.8rem; + padding: 0.4em 0.9em; + border-radius: 8px; + background-color: #ffcc00; + color: var(--otspi-navy); + font-size: 0.65rem; + font-weight: 700; + line-height: 1.4; + white-space: nowrap; +} + +.otspi-cta:hover, +.otspi-cta:focus-visible { + background-color: #ffd83d; + color: var(--otspi-navy); +} + +/* En-tête (écran large, où Material l'affiche) : bouton puis lien vers le dépôt */ +@media screen and (min-width: 60em) { + .md-header__source { + display: flex; + align-items: center; + width: auto; + max-width: none; + } +} + +/* Menu latéral (mobile, où Material l'affiche) : bouton au-dessus du lien vers le dépôt */ +@media screen and (max-width: 76.2344em) { + .md-nav__source { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.6rem; + } + + .md-nav__source .otspi-cta { + margin-right: 0; + } +} + /* -------------------------------------------------------------------------- Titres : hiérarchie nette, sans le gris clair et la graisse légère de Material -------------------------------------------------------------------------- */ diff --git a/mkdocs.yml b/mkdocs.yml index 3fbb9f9..f5355a5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,6 +9,11 @@ edit_uri: edit/main/docs/ copyright: Copyright © 2026 OTSPI — Contenu sous licence Creative Commons Attribution 4.0 International (CC-BY-4.0). +# Liens internes : un lien vers une ancre absente (titre renommé, par exemple) fait échouer +# le build strict, comme un lien vers une page absente. +validation: + anchors: warn + hooks: - hooks/page_lang.py @@ -68,8 +73,11 @@ extra_css: - stylesheets/print.css plugins: + # Recherche en français et en anglais (livre blanc et page des hébergements existent dans les deux langues) - search: - lang: fr + lang: + - fr + - en markdown_extensions: - admonition @@ -118,6 +126,7 @@ nav: - Historique des versions: livre-blanc/historique.md - Statuts et gouvernance: - Projet de statuts: statuts/statuts-association.md + - Historique du projet de statuts: statuts/historique.md - Charte d'éthique: gouvernance/charte-ethique.md - Organisation technique et CPC: gouvernance/comite-technique.md - Règlement intérieur: @@ -139,3 +148,6 @@ nav: - Hébergements: reunions/hebergements.md - Hosting transparency (EN): reunions/hosting.md - Registre des réunions et PV: reunions/index.md + - Participer: + - participer/index.md + - Site de l'association: https://www.otspi.org/ diff --git a/overrides/partials/logo.html b/overrides/partials/logo.html new file mode 100644 index 0000000..f9066b9 --- /dev/null +++ b/overrides/partials/logo.html @@ -0,0 +1,7 @@ +{#- + Logo de l'en-tête : anneau bleu nuit sur l'en-tête blanc du thème clair, anneau blanc sur + l'en-tête bleu nuit du thème sombre (voir extra.css). Le lien parent porte déjà le nom du site : + les images sont décoratives. +-#} + + diff --git a/overrides/partials/source.html b/overrides/partials/source.html index 38a62d4..7b44062 100644 --- a/overrides/partials/source.html +++ b/overrides/partials/source.html @@ -1,7 +1,9 @@ {#- - Lien vers le dépôt, sans les statistiques GitHub : Material les récupère depuis - api.github.com à chaque visite, ce qui contacte un service tiers. Le lien seul est conservé. + Appel à l'action commun avec www.otspi.org, puis lien vers le dépôt, sans les statistiques + GitHub : Material les récupère depuis api.github.com à chaque visite, ce qui contacte un service + tiers. Le partiel sert à la fois à l'en-tête (écran large) et au menu latéral (mobile). -#} +Signer le manifeste
{% set icon = config.theme.icon.repo or "fontawesome/brands/git-alt" %} diff --git a/scripts/zenodo_draft.py b/scripts/zenodo_draft.py new file mode 100644 index 0000000..b07ab51 --- /dev/null +++ b/scripts/zenodo_draft.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: EUPL-1.2 +"""Prépare sur Zenodo un dépôt en brouillon du livre blanc (PDF français et anglais) pour une version donnée. + +Le dépôt reste un brouillon : le DOI n'est attribué, définitivement, que lorsqu'une personne relit puis +publie le dépôt depuis Zenodo. Les métadonnées viennent de .zenodo.json ; la version et la date de +publication sont ajoutées. Appelé par .github/workflows/release.yml à la publication d'une release. + +Variables d'environnement : + ZENODO_TOKEN jeton personnel Zenodo (portées deposit:write et deposit:actions) ; + ZENODO_URL https://zenodo.org par défaut (https://sandbox.zenodo.org pour un essai) ; + ZENODO_DEPOSITION_ID identifiant d'un dépôt déjà publié : le brouillon en devient une nouvelle + version (même DOI de concept) au lieu d'un dépôt distinct. + +Usage : + python3 scripts/zenodo_draft.py --version 0.9 fichier.pdf [...] + python3 scripts/zenodo_draft.py --version 0.9 --dry-run fichier.pdf [...] # affiche sans envoyer + +Aucune dépendance en dehors de la bibliothèque standard. +""" + +import argparse +import datetime +import json +import os +import sys +import urllib.request +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent + + +def api(method, url, token, payload=None, data=None, content_type="application/json"): + body = json.dumps(payload).encode("utf-8") if payload is not None else data + request = urllib.request.Request(url, data=body, method=method) + request.add_header("Authorization", f"Bearer {token}") + if body is not None: + request.add_header("Content-Type", content_type) + with urllib.request.urlopen(request, timeout=300) as response: + raw = response.read() + return json.loads(raw) if raw else None + + +def metadata(version): + meta = json.loads((ROOT / ".zenodo.json").read_text(encoding="utf-8")) + meta["version"] = version + meta["publication_date"] = datetime.date.today().isoformat() + return meta + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--version", required=True, help="numéro de version (par exemple 0.9)") + parser.add_argument("--dry-run", action="store_true", help="afficher les métadonnées et les fichiers sans rien envoyer") + parser.add_argument("files", nargs="+", type=Path, help="fichiers à déposer") + args = parser.parse_args() + + missing = [str(f) for f in args.files if not f.is_file()] + if missing: + sys.exit(f"Fichiers introuvables : {', '.join(missing)}") + meta = metadata(args.version) + if args.dry_run: + print(json.dumps({"metadata": meta, "files": [f.name for f in args.files]}, ensure_ascii=False, indent=2)) + return + + token = os.environ.get("ZENODO_TOKEN") + if not token: + sys.exit("ZENODO_TOKEN n'est pas défini.") + base = os.environ.get("ZENODO_URL", "https://zenodo.org").rstrip("/") + previous = os.environ.get("ZENODO_DEPOSITION_ID") + + if previous: + # Nouvelle version d'un dépôt publié : Zenodo recopie ses fichiers, qu'on remplace + created = api("POST", f"{base}/api/deposit/depositions/{previous}/actions/newversion", token) + draft = api("GET", created["links"]["latest_draft"], token) + for old in draft.get("files", []): + api("DELETE", old["links"]["self"], token) + else: + draft = api("POST", f"{base}/api/deposit/depositions", token, payload={}) + + for path in args.files: + api("PUT", f'{draft["links"]["bucket"]}/{path.name}', token, data=path.read_bytes(), content_type="application/octet-stream") + draft = api("PUT", f'{base}/api/deposit/depositions/{draft["id"]}', token, payload={"metadata": meta}) + + link = draft["links"]["html"] + print(f"Brouillon Zenodo prêt (non publié, aucun DOI attribué) : {link}") + summary = os.environ.get("GITHUB_STEP_SUMMARY") + if summary: + with open(summary, "a", encoding="utf-8") as out: + out.write(f"Brouillon Zenodo de la version {args.version} : {link}\n\n" + "À relire puis publier depuis Zenodo pour attribuer le DOI.\n") + + +if __name__ == "__main__": + main()