Spaces:
Running
Running
File size: 6,499 Bytes
628d92a 7d5b986 628d92a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | # Procédure de release
> Sprint A9 du plan de remédiation institutionnelle
> ([`docs/audits/remediation-plan-2026-05.md`](../audits/remediation-plan-2026-05.md)).
> Étendu au Sprint S6 (déploiement institutionnel BnF).
## Pré-requis avant tout tag (Sprint S6)
| Vérif | Commande | Cible |
|---|---|---|
| Tests verts | `pytest tests/ -q` | 0 failed |
| Lint propre | `ruff check picarones/ tests/` | All checks passed |
| Type strict | `python -m mypy picarones/domain/` | 0 erreur |
| Sécurité statique | `bandit -ll -r picarones/` | 0 HIGH |
| CVEs deps | `pip-audit` | aucune CVE non-mitigée dans le runtime |
| CHANGELOG | section `## [X.Y.Z] — YYYY-MM-DD` présente | |
| Compteurs doc | `python scripts/gen_readme_tables.py --check` | exit 0 |
## Mode public vs institutionnel
Pour un déploiement BnF (mode institutionnel), s'assurer que les
variables d'environnement de production sont prêtes **avant** de
tagger. L'app refuse de démarrer sans (Sprint S6.9).
| Variable | Public (HF Space) | Institutionnel |
|---|---|---|
| `PICARONES_PUBLIC_MODE` | `1` | non set |
| `PICARONES_CSRF_REQUIRED` | non set | `1` |
| `PICARONES_CSRF_SECRET` | non set | **OBLIGATOIRE** |
| `PICARONES_LOG_FORMAT` | non set | `json` (recommandé pour ops) |
### Génération du CSRF secret
```bash
# 32 bytes hex
openssl rand -hex 32
# Persister dans le secret manager institutionnel :
# - Vault : ``vault kv put secret/picarones csrf=<hex>``
# - AWS Secrets Manager : ``aws secretsmanager create-secret``
# - Kubernetes : ``kubectl create secret generic picarones-csrf``
# - Docker Compose : ``.env`` non versionné
```
**Ne JAMAIS** committer ce secret dans un Dockerfile, un
docker-compose.yml versionné, ou un dépôt git public.
## Vue d'ensemble
Une release Picarones produit **trois artefacts** :
1. Un wheel + sdist sur **PyPI** (`pip install picarones==X.Y.Z`).
2. Une image Docker **multi-arch** sur ghcr.io
(`docker pull ghcr.io/maribakulj/picarones:X.Y.Z`).
3. Une **GitHub Release** avec le sdist/wheel attachés et les
release notes extraites du `CHANGELOG.md`.
Le pipeline est entièrement automatisé : il suffit de pousser un
tag `v*.*.*` pour déclencher l'enchaînement complet (workflow
[`.github/workflows/release.yml`](../../.github/workflows/release.yml)).
## Procédure release standard
### Pré-requis (une fois)
1. **PyPI Trusted Publisher** : sur <https://pypi.org/manage/account/publishing/>,
ajouter ce repo + workflow `release.yml` + environnement `pypi`.
Idem pour TestPyPI dans l'environnement `testpypi`.
2. **GitHub repo** : créer les environnements `pypi` et `testpypi`
dans Settings → Environments, et marquer `pypi` comme "required
reviewers" si vous voulez une validation manuelle finale.
3. **GHCR** : `packages: write` sur `GITHUB_TOKEN` est natif (rien
à configurer).
### Cycle de release
```bash
# 1. Vérifier que main est vert + à jour
git checkout main
git pull --ff-only
# 2. Mettre à jour le CHANGELOG.md (Keep a Changelog)
# Ajouter une section ## [1.2.0] — YYYY-MM-DD avec les changes
git add CHANGELOG.md
git commit -m "docs(changelog): release 1.2.0"
# 3. Tag annoté + push
git tag -a v1.2.0 -m "Picarones 1.2.0"
git push origin main
git push origin v1.2.0
# 4. Surveiller le workflow Actions
gh run watch
```
Le workflow déroule **automatiquement** :
1. **build** — sdist + wheel via setuptools_scm (version dérivée du tag),
`twine check`, smoke test wheel install.
2. **publish-testpypi** — upload TestPyPI via OIDC trust.
3. **testpypi-smoke** — installation depuis TestPyPI dans un container
vierge + `picarones demo`.
4. **publish-pypi** — upload PyPI via OIDC trust (production).
5. **docker** — build multi-arch (linux/amd64 + linux/arm64) avec
QEMU, push ghcr.io, attestations SLSA + SBOM.
6. **github-release** — création de la Release GitHub avec corps
extrait depuis la section CHANGELOG correspondante.
Durée totale : ~15 min (multi-arch + 30s d'indexation TestPyPI).
## Versionnement
Picarones suit **Semantic Versioning 2.0.0** :
- `MAJOR.MINOR.PATCH` — incompatibilité, ajout, fix.
- Suffixes pré-release : `-rc1`, `-beta1`, `-alpha1`. Le workflow
les détecte et coche `prerelease=true` sur la GitHub Release.
`setuptools_scm` dérive automatiquement la version du tag git :
| Contexte | Version produite |
|---|---|
| Tag `v1.2.0` | `1.2.0` |
| 5 commits après `v1.2.0` | `1.2.1.dev5+g<sha>` (dev seulement) |
| `v1.3.0-rc1` | `1.3.0rc1` (PEP 440) |
## Procédure d'urgence : hotfix sécurité
Pour un fix CVE qui doit sortir en < 72 h (politique GOVERNANCE.md) :
```bash
git checkout -b hotfix-cve-2026-XXXX main
# correctif minimal + test
git commit -m "fix(security): patch CVE-2026-XXXX"
# CHANGELOG bump
git commit -m "docs(changelog): release 1.2.1"
git tag -a v1.2.1 -m "Picarones 1.2.1 (security)"
git push origin hotfix-cve-2026-XXXX v1.2.1
# Le workflow release.yml gère le reste.
# Après merge : annonce sur SECURITY.md + courriel mainteneur.
```
## Yanking d'une release publiée
PyPI permet de retirer (yank) une version compromise sans la
supprimer. À utiliser si une release introduit une régression
critique :
```bash
# Connexion à PyPI → Manage → version concernée → "Yank"
# Justification dans le commentaire (visible publiquement).
```
L'image ghcr.io reste — mais le tag `:latest` ne pointera plus vers
la version yankée si on pousse une nouvelle release.
## Validation post-release
Checklist 30 min après la fin du workflow :
- [ ] `pip install picarones==<version>` fonctionne dans un venv frais.
- [ ] `docker run ghcr.io/maribakulj/picarones:<version>` démarre
sans erreur et expose `/health`.
- [ ] La GitHub Release affiche bien les release notes attendues.
- [ ] `cffconvert --validate` confirme que `CITATION.cff` cite la
bonne version (Sprint A12+).
## Annexe : rollback complet
Si la release est compromise et doit être retirée intégralement :
1. PyPI : yank la version (cf. plus haut).
2. ghcr.io : `docker manifest rm ghcr.io/maribakulj/picarones:<version>`.
3. GitHub Release : passer en draft + ajouter un README explicatif.
4. Tag git : `git push --delete origin v<version>` puis nouveau
tag `v<version>+1` corrigé (un tag git ne peut pas être réécrit
sans casser tous les checkouts existants — préférer le bump).
Ne **jamais** force-push un tag déjà publié — les utilisateurs qui
ont fait `git fetch` voient un conflit.
|