Skip to content

🔖 chore(release): v1.0.0 - #29

Merged
kbrdn1 merged 34 commits into
mainfrom
dev
Aug 14, 2026
Merged

🔖 chore(release): v1.0.0#29
kbrdn1 merged 34 commits into
mainfrom
dev

Conversation

@kbrdn1

@kbrdn1 kbrdn1 commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Summary

Promotion de la 1.0.0 vers main. Le candidat v1.0.0-rc.2 a validé toute la chaîne sur preprod :

$ curl https://pre-prod.kbrdn.dev/api/health
{"status":"ok","version":"1.0.0-rc.2","sha":"7dd0c079…","env":"preprod"}

Après ce merge, le tag v1.0.0 sera posé sur main — jamais avant, release.yml refuse d'ailleurs un tag stable qui n'est pas un ancêtre de main.

Related Issue

Closes #26

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update
  • ♻️ Refactoring (no functional changes)
  • ⚡️ Performance improvement
  • 🎨 Style/UI update

Scope

  • Homepage
  • Blog
  • Projects
  • UI Components
  • i18n / Translations
  • SEO
  • Configuration

Changes

28 commits, dont l'essentiel :

Détail complet : changelogs/1.0.0.md.

Tests

  • release.yml validé de bout en bout sur v1.0.0-rc.2 : resolve → build → deploy preprod → pre-release, les 4 jobs verts
  • Preprod sert la bonne version, le bon sha et le bon environnement
  • Les gates ont mordu pour de vrai : le rc.1 a échoué sur un bug du workflow, corrigé avant d'aller plus loin — c'est le rôle de l'étage candidat
  • Le déploiement preprod par deploy.yml fonctionne depuis la synchro VPS
  • CI verte sur chacune des 6 PR de ce cut
  • Le déploiement prod n'a pas encore tourné sous le nouveau script — c'est le tag v1.0.0 qui le fera

Checklist

  • My code follows the project's coding style
  • I have performed a self-review of my code
  • I have tested my changes locally
  • My changes generate no new warnings
  • I have updated the documentation if needed
  • TypeScript compiles without errors — ci.yml vert sur chaque PR
  • Linting passes — idem

Additional Notes

⚠️ Ce merge déclenche deploy.yml vers prod (image :main), puis le tag déclenchera release.yml vers prod (image :v1.0.0). Les deux partagent la clé de concurrence vps-deploy-prod ajouté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.

kbrdn1 and others added 30 commits July 25, 2026 15:10
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
kbrdn1 and others added 3 commits August 14, 2026 19:02
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
🔀 chore(sync): rattrape main dans dev avant la promotion v1.0.0
@kbrdn1
kbrdn1 merged commit 1e512ce into main Aug 14, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔖 [Release]: v1.0.0

1 participant