Conversation
Le favicon précédent était hors design system : conteneur circulaire alors que --ui-radius vaut 0, dégradé #FFAA00 → #C5690C absent de la palette, et trois ombres portées floutées illisibles à 16 px. Le nouveau mark fait porter deux lectures par les mêmes traits : le cadre ouvert en bas-droite lit comme l'arobase du wordmark, et son montant gauche sert de barre au K, auquel le chevron vient s'accrocher. Le cadre est fait de quatre <rect> pleins plutôt que d'un tracé en stroke : satori ne rend pas les strokes de façon fiable, et partager une seule géométrie évite que la version runtime et la version OG divergent. Rendus comparés au pixel près, identiques. - app/components/ui/Logo.vue : <UiLogo>, neutre en currentColor - public/favicon.svg : valeurs figées + @media (prefers-color-scheme) - public/favicon.ico : regénéré en 16/32/48/64/128/256 - Header et BlogHeader : les quatre boutons brand, pas les dropdowns - OgImage/*.satori.vue : SVG inline, hors du composant Le .ico fige le neutre en #1a1a1a : correct sur onglet clair, invisible sur onglet sombre. Le SVG est servi en premier, le .ico ne sert que de repli pour les vieux clients et les crawlers. Refs #12 Claude-Session: https://claude.ai/code/session_011tQX9U1mEk7iux8G5R5vVz
Annonce la mise en ligne du site de doc de gwm et détaille comment il est construit : la doc reste dans gwm-cli et le site n'en est qu'un miroir, le pont de synchronisation entre les deux repos, les écarts de format Nuxt Content → Starlight que la conversion répare, le monorepo multi-produits, la génération des cartes Open Graph, et ce qui n'est pas construit. Article bilingue (slug gwm-docs), avec ses assets localisés par locale : bannière au kit Claude Dark, captures du site et carte OG en fr et en. Refs #14 Claude-Session: https://claude.ai/code/session_01DvpYNxEuX7J4WuBJoFHGE8
Les articles gwm et gwm-docs annonçaient gwm-cli sous licence MIT seule. Le projet est dual-licensed (`license = "MIT OR Apache-2.0"` dans Cargo.toml, LICENSE-MIT + LICENSE-APACHE à la racine) — c'est la moitié Apache qui porte la clause de brevet explicite. Dans gwm-docs, la phrase élidait le verbe et le complément (« le produit sur gwm-cli »), ce qui étendait le MIT du site au produit : les deux clauses sont maintenant dissociées. La licence de kbrdn-docs reste MIT, inchangée. closes #16
📝 docs(blog): annonce gwm-cli en double licence MIT ou Apache-2.0
…elog Le repo n'avait aucun changelog. Layout repris de gwm-cli et kbrdn-docs : root = section [Unreleased] en cours seule + index ## Past releases, un fichier par version sous changelogs/, les candidats sous changelogs/pre-releases/. .claude/ était entièrement gitignoré, donc la commande /changelog du projet serait morte avec le worktree. La négation cible .claude/commands/ — et exclut .claude/* plutôt que .claude/, sinon git n'entre jamais dans le dossier pour voir la négation. refs #18
package.json n'avait aucun champ version — la cible d'un bump n'existait pas. Départ à 1.0.0 comme kbrdn-docs : le site tourne déjà en production, un 0.x mentirait sur son état. Cette valeur est la seule vérité de version du repo : elle alimente /api/health et le tag doit la refléter (v<version>). refs #18
Impossible jusqu'ici de répondre à « quelle version tourne sur preprod ».
GET /api/health renvoie { status, version, sha, env } ; version et sha sont
figés au build (ARG APP_VERSION / GIT_SHA), donc l'image est
auto-descriptive — une valeur lue à l'exécution décrirait le conteneur, pas
l'artefact. env reste réglable au run, la même image servant les deux
étages.
Le HEALTHCHECK quitte `/` pour cette route : la home rend côté serveur même
quand la couche API est morte, l'ancienne sonde passait donc au vert sur un
conteneur à moitié cassé.
Il utilise `wget -O /dev/null` et non `--spider` : --spider émet un HEAD, et
h3 ne route pas HEAD vers un handler .get — vérifié sur le build de prod,
HEAD /api/health répond 404 quand GET répond 200.
refs #18
v1.1.0-rc.N construit une image immutable :v1.1.0-rc.N, la déploie sur
preprod et ouvre une pre-release ; v1.1.0 fait de même vers prod. deploy.yml
n'est pas touché : il déclenche sur push de branche, jamais sur un tag, donc
l'itération quotidienne dev → preprod reste telle quelle.
Un seul workflow pour les deux étages, là où gwm-cli en a deux : chez lui
les matrices de build diffèrent, ici c'est la même image et seuls changent
la cible, --prerelease et le chemin des notes.
L'aiguillage vit dans scripts/resolve-release-tag.sh plutôt qu'inline en
YAML — s'y tromper publierait un candidat non validé sur kbrdn.dev, donc il
est rejouable hors CI et couvert par resolve-release-tag.test.sh (22 cas,
dont les tags mal formés qui ne doivent jamais retomber dans la branche
prod). Le test tourne en bash 3.2, d'où tr plutôt que ${var,,}.
Deux gates avant de publier : package.json doit porter la version cible du
tag, et le fichier de notes doit exister — échouer avant le déploiement
plutôt qu'après une release déjà visible. Et la release n'est créée qu'une
fois /api/health confirmé depuis l'extérieur qu'il sert bien cette version
sur ce domaine.
refs #18
Le rollback ne pouvait pas fonctionner : la fin de deploy supprimait la couleur sortante (docker rm -f), alors que --rollback exige justement ce conteneur. Au repos une seule couleur tournait, donc l'option échouait systématiquement — d'autant plus silencieusement que personne ne s'en sert avant d'en avoir besoin. La couleur sortante est désormais stoppée et conservée, et --rollback la redémarre puis attend healthy avant de basculer nginx. Pas d'accumulation : le rm -f en début de deploy nettoie celle du cycle précédent. Deux chemins de retour arrière, documentés dans l'en-tête : --rollback remonte d'un cran, --image ...:vX.Y.Z vise une version précise et survit à une perte des conteneurs puisque les tags de version sont immutables. Le conteneur porte un label kbrdn.version (lu du tag de l'image) et reçoit NUXT_APP_ENV — et pas APP_ENV : une fois le build figé, runtimeConfig ne se laisse surcharger que par la convention NUXT_<CLÉ>, vérifié sur le build de prod. Sa sonde vise /api/health comme celle du Dockerfile. L'attente healthy devient une fonction, le rollback en avait besoin aussi. refs #18
docs/RELEASE.md décrit le flow complet et surtout les deux contraintes que le protocole générique n'a pas et qui bloquent le premier cut si on les découvre en route : main exige un historique linéaire, donc le dev → main en merge commit ne passe pas tel quel ; et le check Linked issue impose qu'une PR vers main ferme une issue, donc chaque version a besoin de la sienne. Consigne aussi le point ouvert du wrapper VPS : deploy.yml et release.yml appellent une commande nue configurée par variables d'environnement, alors que scripts/deploy.sh parse des flags — les deux ne peuvent pas décrire le même binaire, et tant que ce n'est pas tranché les changements de deploy.sh peuvent n'avoir aucun effet sur ce qui tourne réellement. refs #18
… conteneur Le champ env de /api/health devait venir de -e NUXT_APP_ENV, posé par scripts/deploy.sh. Sauf que le déploiement passe par un wrapper sur le VPS qui n'est pas ce fichier — donc la variable ne serait jamais arrivée, env serait resté à "local", et l'assertion du job de vérification aurait échoué sur chaque release. Après le swap de prod : déploiement réussi, release rouge, pas de release publiée. APP_ENV devient un build-arg passé par les deux workflows. Vérifié sur le build de prod : sans aucune variable au runtime, /api/health renvoie bien version, sha et env du build. Le gate ne dépend plus que de ce que la CI contrôle elle-même. deploy.yml le passe aussi, sinon preprod annoncerait env=local en permanence — c'est lui qui la déploie au quotidien. Sans APP_VERSION en revanche : une image de branche n'a pas de version de release, le fallback package.json est le bon. docs/RELEASE.md liste maintenant ce qui prend effet au merge (tout ce qui est cuit dans l'image ou exécuté côté CI) et ce qui attend la synchro du VPS — dont le rollback, qui reste cassé jusque-là. refs #18
Diagnostic du VPS en lecture seule : il n'y a pas de copie driftée de
deploy.sh, /srv/deploy.sh est bit-pour-bit identique à celui du repo. Ce qui
réconcilie les deux interfaces — le workflow qui configure par variables
d'environnement, deploy.sh qui attend des flags — c'est un ForceCommand dans
authorized_keys pointant sur /srv/github-deploy.sh, qui exporte les
clé=valeur et traduit le premier token nu en action.
Ce maillon n'était versionné nulle part : la pièce centrale du pipeline
était invisible depuis le repo et non reproductible. Sa copie de référence
entre ici. Elle n'est pas déployée automatiquement — le ForceCommand
n'autorise que deploy|rollback|status|ping, donc ni scp ni rsync par la clé
du CI — d'où la procédure de synchronisation manuelle documentée.
Le point ouvert devient donc une étape de déploiement connue, et non une
inconnue : les changements de scripts/deploy.sh ne sont pas inertes, ils
prennent effet à la synchro. La table dit lesquels attendent.
Le diagnostic confirme aussi le rollback cassé : une seule couleur par
environnement, docker ps -a ne liste même pas les opposées. Et le SSH de
GitHub Actions est le seul déclencheur — ni Dokploy, ni webhook, ni cron.
Deux suites notées sans être faites, faute de pouvoir les tester d'ici :
durcir l'export de github-deploy.sh par une allowlist (PATH est exportable,
et deploy.sh résout docker/nginx/systemctl par le PATH, en root), et
retrouver les blocs server{} nginx qui consomment les upstreams.
closes #18
…s stables Cinq findings de la review Codex, tous réels — trois attaquaient des propriétés que cette PR promet. Déploiements concurrents (P1). Le protocole lui-même les provoque : le merge dev → main réveille deploy.yml vers prod, le push du tag qui suit réveille release.yml vers prod, et leurs groupes de concurrence au niveau workflow étant distincts, rien ne les sérialisait. Deux deploy.sh --env prod visent les mêmes noms de conteneurs et le même upstream nginx — le second détruit le conteneur du premier, l'image active à l'arrivée est indéterminée. Les deux jobs partagent maintenant la clé vps-deploy-<env> ; les groupes de concurrence sont globaux au repo, donc ça suffit à les mettre en file. Tag stable hors main (P1). Rien n'empêchait de poser vX.Y.Z sur dev avant le merge : package.json et les notes y sont déjà, les deux gates passaient, et prod recevait un commit non fusionné. docs/RELEASE.md l'interdisait, c'était une consigne sans garde-fou — le workflow exige désormais que le tag stable soit un ancêtre de origin/main. Les candidats ne sont pas concernés, vivre sur dev est leur raison d'être. Réécriture des images (P1). Un workflow_dispatch sur un tag existant republiait la même référence GHCR. Les tags de registre sont mutables et le build n'est pas reproductible (oven/bun:1-alpine bouge, bun install n'est pas figé), donc :vX.Y.Z pouvait cesser de désigner l'artefact publié — soit exactement la propriété sur laquelle repose le rollback par version. Le build est sauté si l'image existe, l'original est redéployé tel quel. Sha du tag (P2). github.sha désigne la branche de lancement sur un workflow_dispatch, pas le tag : /api/health aurait annoncé un sha sans rapport avec le contenu de l'image. Lu par git rev-parse après le checkout. Healthcheck compose (P2). docker-compose.yml remplace entièrement celui du Dockerfile et gardait l'ancienne sonde sur `/` — le faux positif visé par cette PR survivait donc dans le chemin `docker compose up` documenté. refs #18
…potente Deux findings, tous deux sur le garde-fou ajouté à la passe précédente. Lecture du registre (P1). `docker manifest inspect` renvoie non-zéro aussi bien pour un manifeste absent que pour un timeout, une panne GHCR ou une erreur d'authentification — le bloc en déduisait « l'image n'existe pas » et repartait sur un build. Dès le retour de l'accès, le push écrasait un tag déjà publié, soit exactement l'immuabilité que ce garde-fou protège. Les motifs d'absence sont désormais reconnus explicitement ; tout autre échec fait tomber le job. Republication (P2). Le scénario est documenté (workflow_dispatch sur un tag sorti) mais `gh release create` échouait sur une release existante, après un redéploiement pourtant réussi. On bascule sur `gh release edit`. Le flag booléen explicite `--prerelease=$PRERELEASE` remplace au passage le tableau de flags conditionnel, ce qui supprime la construction qui piégeait `set -e`. refs #18
Le wrapper faisait `export "$tok"` sur n'importe quelle variable envoyée dans SSH_ORIGINAL_COMMAND. PATH en fait partie — et deploy.sh résout docker, nginx et systemctl par le PATH, en root : un `PATH=/tmp/x deploy` exécutait des binaires choisis par l'appelant. Idem LD_PRELOAD, BASH_ENV, IFS. L'escalade suppose déjà la clé privée SSH, donc quelqu'un qui peut de toute façon déployer l'image de son choix en root — la porte n'en avait pas moins de raison de rester ouverte. Seules ENV, IMAGE, GITHUB_TOKEN, RESEND_API_KEY et NUXT_STUDIO_TOKEN passent, soit exactement ce que construisent deploy.yml et release.yml. Refus explicite plutôt qu'ignorance silencieuse : une variable inattendue signale un workflow désynchronisé, ça doit échouer visiblement. Check dans scripts/github-deploy.test.sh — les quatre vecteurs de détournement, la commande réelle des deux workflows, et le cas des secrets vides. Il s'exécute sans toucher deploy.sh (chemins `ping` et rejet). refs #18
Deux findings sur release.yml. Ref ambiguë (P1). `actions/checkout` résout un nom nu comme n'importe quelle ref : une branche nommée v1.2.3 satisfaisait le workflow, et `gh release create` créait au besoin le tag manquant depuis la branche par défaut — la release pointait alors un autre commit que l'image déployée. Les trois checkouts visent maintenant refs/tags/<tag>, ce qui impose un tag et fait échouer le job s'il n'existe pas, et `--verify-tag` empêche gh d'en créer un. Sha non vérifié (P2). Le contrôle externe comparait version et env, pas le commit. Or l'image :main construite après le tag porte le même package.json tant que personne n'a bumpé : un déploiement resté sans effet passait donc le contrôle de version alors que l'artefact tagué ne servait pas le trafic. Le sha résolu depuis le tag remonte du job build et complète la vérification. refs #18
…rmat Deux findings sur .claude/commands/changelog.md, tous deux confirmés en lisant le code de la skill changelog-generator. Perte de données (P1). `split --clean` supprime tout .md de changelogs/ absent du CHANGELOG.md courant (findOrphans, splitter.go:242). Comme le root ne garde qu'[Unreleased] par construction, le deuxième cut effaçait toutes les versions déjà publiées — et le tag suivant serait tombé sur le test -f "$NOTES" de release.yml. Format (P2). renderVersion réécrit le contenu en « # Version X.Y.Z » + ## Features/Fixes/Chores, ne retient comme détails que les lignes préfixées > et déduit la catégorie d'un marqueur en gras : les sections ### Added sont ignorées, la prose est perdue. La commande annonçait pourtant du Keep a Changelog. Preuve que le split n'est pas ce qui produit ces fichiers : ceux de gwm-cli et kbrdn-docs sont en « # [X.Y.Z] - date » avec ### Added et des paragraphes entiers — ils sont écrits à la main. La commande décrit donc la migration réelle : rassembler la matière avec git log, rédiger sous [Unreleased], déplacer la section vers le fichier de version, réamorcer le root. Une copie de section coûte moins cher que contourner un outil qui vise autre chose. changelog.config.json partait avec — il ne configurait que ce split. refs #18
…ck raté Deux asymétries dans le code ajouté par cette PR. Ascendance (P2). Le contrôle ne s'appliquait qu'aux tags stables. Un vX.Y.Z-rc.N posé sur une branche feature non fusionnée passait donc les gates et publiait une pre-release GitHub que dev ne permet pas de reproduire. Le contrôle vaut maintenant pour les deux étages : main pour un stable, dev pour un candidat. Rollback (P2). En cas d'échec de la sonde après redémarrage, le conteneur était laissé tel quel. Il porte --restart unless-stopped, donc il survivait — ou bouclait — sur un port que plus rien ne sert, jusqu'au prochain deploy qui l'aurait nettoyé. Le chemin de deploy faisait déjà l'analogue avec son docker rm -f ; le rollback le stoppe désormais avant d'abandonner. Écarté : « comparer un digest propre à l'image ». Sur une release stable, :main et :vX.Y.Z sortent du même commit — mêmes version, sha et env — donc le contrôle externe ne peut pas les distinguer. Mais ils servent le même code : le scénario suppose un déploiement sans effet ET un jumeau déjà en place, pour un écart observable nul. Le digest n'existant qu'après le build, l'injecter demanderait une valeur redondante avec APP_VERSION. refs #18
L'ordre des étapes rendait le candidat impossible : l'étape 1 migrait [Unreleased] vers le fichier stable et vidait la section racine, puis l'étape 3 demandait de photographier cette même section pour le rc — donc des notes de pré-release vides, et un tag rc qui échoue sur le test -f "$NOTES" de release.yml. Le bump passe en premier, le candidat ensuite, la migration seulement après validation. Documente aussi l'éviction possible d'un run en attente : GitHub Actions ne conserve qu'un job en attente par groupe de concurrence, donc trois déploiements qui se chevauchent sur un même environnement peuvent en annuler un. Le rattrapage est le workflow_dispatch sur le tag, rendu idempotent plus tôt dans cette PR — plutôt qu'un verrou distribué dont le remède serait plus risqué que le mal. refs #18
…métrique Trois findings. `denied` (P1). Il était listé parmi les motifs d'absence d'image, alors qu'il signale aussi un incident d'authentification sur un tag qui existe : si l'accès revenait avant le push, le job reconstruisait et écrasait un tag censé immuable. Il rejoint donc les erreurs indéterminées, fail-closed. Ça ne bloque pas le premier tag : le package ghcr.io/kbrdn1/kbrdn.dev existe déjà (:main et :dev y sont publiées), donc un tag absent répond « manifest unknown ». Zéros initiaux (P2). `[0-9]+` acceptait v1.0.0-rc.01 et v01.0.0, que SemVer interdit — le cœur correspondait à package.json, donc le workflow les déployait. La regex exige maintenant `0` ou un nombre commençant par 1-9, sur les trois composantes et sur l'identifiant de pré-release. Six cas ajoutés au test, dont v0.1.0 et v1.0.0-rc.10 pour vérifier que les zéros légitimes passent toujours. nginx -t au rollback (P2). Le bloc de restauration sautait explicitement le nettoyage pour l'action rollback : le conteneur relancé restait actif sous --restart unless-stopped sur un port que l'upstream ne sert plus. Même asymétrie que celle corrigée sur le chemin wait_healthy — un deploy détruit sa cible, un rollback la stoppe. refs #18
🔧 chore(ci): init du flow de release avec étage preprod
required_linear_history a été désactivé sur main pour aligner le repo sur gwm-cli et kbrdn-docs, et sur le protocole générique me:release — qui merge dev → main en merge commit. La doc décrivait encore l'ancien état et prescrivait un rebase-merge : le suivre aujourd'hui aplatirait les commits atomiques sans raison. Les « deux contraintes » deviennent donc une seule, le check Linked issue, qui lui n'a pas bougé — toute PR vers main doit fermer une issue, la PR de release comprise. closes #20
📝 docs(release): main accepte de nouveau les merge commits
La synchro du VPS a fait échouer le déploiement qu'elle croisait : bash lit un script par offset, donc remplacer /srv/deploy.sh en cours d'exécution lui fait reprendre sa lecture au même offset dans le nouveau texte. Le script est mort entre le passage healthy et le swap nginx — preprod a continué de servir l'ancienne image pendant que la nouvelle tournait, healthy, sur un port que rien ne routait. La procédure copie maintenant en .new puis remplace d'un mv, qui échange l'inode et laisse l'exécution en cours finir sur l'ancien. Encadrent la copie : une vérification qu'aucun run n'est en vol avant, et que le dernier est vert après. Le rattrapage est documenté — relancer deploy.yml détruit la couleur cible en début de course et emporte l'orphelin. Corrige au passage une ligne vide manquante avant « La chaîne de déploiement », qui collait le titre au paragraphe précédent. closes #22
📝 docs(release): écraser deploy.sh pendant qu'il tourne le casse
Le README s'ouvrait sur un titre et une ligne de description. Un portfolio mérite mieux sur son premier écran. Générée par la skill me:banner-repo, thème Claude Dark, dark + light avec bascule sur prefers-color-scheme et chemins relatifs — chaque branche affiche donc sa propre image. Contenu tiré du repo, rien d'inventé : version lue dans package.json, stack réelle (Nuxt 4 + Nuxt UI v4, contenu markdown, i18n FR/EN, OG images) et la commande de démarrage du README. Le motif à droite est le monogramme du site lui-même, repris de public/favicon.svg — son chevron portait déjà le #d4825d du thème. Son cadre passe en gris moyen : le motif est statique entre les deux rendus, alors que le favicon bascule sur prefers-color-scheme et disparaîtrait donc sur l'un des deux fonds. La config de génération est versionnée à côté des PNG, pour pouvoir régénérer sans la reconstruire de mémoire. closes #24
💄 feat(readme): bannière promo en tête du README
Première version taguée. package.json portait déjà 1.0.0 depuis la mise en place du flow, il n'y a donc pas de bump : ce commit ne fait que migrer les notes. [Unreleased] part dans changelogs/1.0.0.md, la racine repart vide et gagne son index Past releases. Le candidat 1.0.0-rc.1 photographie le même contenu — pour un premier cut les deux coïncident, et release.yml exige que le fichier de notes existe avant de déployer quoi que ce soit. Les .gitkeep des deux dossiers disparaissent, ils ont maintenant du contenu. refs #26
🔖 chore(release): v1.0.0
Le premier tag candidat a fait échouer le job build sans la moindre sortie.
Cause : le step tourne sous `bash -e {0}` — imposé par GitHub Actions, visible
dans le log — et `set -uo pipefail` ne retire pas ce `-e`. L'affectation
`OUT=$(docker manifest inspect …)` tuait donc le step dès que la commande
renvoyait non-zéro, c'est-à-dire précisément dans le cas nominal du premier
tag, quand l'image n'existe pas encore. Le `if` qui suivait n'était jamais
atteint, d'où l'absence totale de trace.
Passage à `if OUT=$(…)`, qui met la commande en contexte conditionnel où
`set -e` ne mord pas.
La branche « image absente » trace maintenant la réponse du registre. On ne
sait toujours pas comment GHCR formule cette absence — le step est mort avant
de l'afficher — et cette réponse décide si `denied` doit rejoindre les motifs
d'absence ou rester traité comme une erreur indéterminée. Il reste dehors
tant que la preuve manque : c'est le sens du fail-closed.
Notes du candidat rc.2, le rc.1 n'ayant rien publié.
refs #26
🐛 fix(ci): GitHub Actions impose bash -e, le set -uo ne le désactive pas
Le commit 6eb8008 (publication de gwm.kbrdn.dev, #15) a été fait sur main sans passer par dev. Les deux branches ont donc créé indépendamment app/content/{fr,en}/blogs/gwm-docs.mdx, d'où un conflit add/add qui rendait la PR dev → main non mergeable. Une seule ligne diffère sur 144, et la version de dev est la bonne : elle mentionne la double licence MIT ou Apache-2.0 de gwm-cli, actée depuis. La version de main est antérieure à ce changement et dit seulement « le produit sur github.com/kbrdn1/gwm-cli ». Résolution en gardant dev pour les deux langues. refs #26
24 tasks
🔀 chore(sync): rattrape main dans dev avant la promotion v1.0.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Promotion de la
1.0.0versmain. Le candidatv1.0.0-rc.2a validé toute la chaîne sur preprod :Après ce merge, le tag
v1.0.0sera posé surmain— jamais avant,release.ymlrefuse d'ailleurs un tag stable qui n'est pas un ancêtre demain.Related Issue
Closes #26
Type of Change
Scope
Changes
28 commits, dont l'essentiel :
vX.Y.Z-rc.N→ preprod + pre-release,vX.Y.Z→ prod + release, images immutables par versionGET /api/health—{ status, version, sha, env }, tout figé au build ; leHEALTHCHECKy pointe au lieu de/scripts/github-deploy.shversionné et durci — allowlist des variables,PATHcomprisdocs/RELEASE.md— protocole, chaîne de déploiement réelle, synchronisation VPSDétail complet :
changelogs/1.0.0.md.Tests
release.ymlvalidé de bout en bout surv1.0.0-rc.2: resolve → build → deploy preprod → pre-release, les 4 jobs vertsrc.1a échoué sur un bug du workflow, corrigé avant d'aller plus loin — c'est le rôle de l'étage candidatdeploy.ymlfonctionne depuis la synchro VPSv1.0.0qui le feraChecklist
ci.ymlvert sur chaque PRAdditional Notes
deploy.ymlvers prod (image:main), puis le tag déclencherarelease.ymlvers prod (image:v1.0.0). Les deux partagent la clé de concurrencevps-deploy-prodajoutée dans #19 : ils seront sérialisés au lieu de se disputer les mêmes conteneurs. C'est aussi la première mise à l'épreuve réelle de ce garde-fou.Le rollback prod ne redeviendra utilisable qu'après deux déploiements sous le nouveau script — il lui faut une couleur sortante conservée. D'ici là, retour arrière via
--image ghcr.io/kbrdn1/kbrdn.dev:vX.Y.Z.